Due to an unsolved issue in my current project [ Weird scrollbar UI in hosted WPF composite control ], I've had to defer to workarounds. One such idea I came up with was to keep the ScrollViewer in question in sync with a Windows Forms ScrollBar (horizontal and vertical). How would I go about doing that?
In essence, I want the WinForms scrollbar(s) to work like the ScrollViewers (in terms of dragging and events suchlike).
I think there's no any other way than send/listen to the lo-level API Windows messaging. You should have a decent knowledge of the Win APIs, and how to manage them. I'd add also that the scrollers are mid-complexity task to manage, but maybe it's me...
Here is the vertical scroll message reference.
I managed to implement it without having to much around with the WinAPI. Here's how I did it:
Registered a ScrollOffsetChanged event handler with the scrollviewer (the composite control that implemented the IScrollInfo interface exposed the event).
Registered a ValueChanged event handler with the windows forms scrollbar.
A couple of signal flags to prevent cyclic calls to the event handler.
The first handler called an UpdateExternalScrollBars() function that calculated the Maximium Scrollbar property using the following expression: ScrollViewer->ExtentHeight/Width - ScrollViewer->ViewportHeight/Width. The Value property was directly set to ScrollViewer->Vertical/HorizontalOffset.
The second fetched the Value property from the windows forms scrollbar and passed it to ScrolViewer->ScrollToVertial/HorizontalOffset(). The signal flags were appropriately set beforehand.
Related
In my WPF application, I am using the ViewModelLocator without IoC. I am calling the static ViewModelLocator.Cleanup() method provided by the MVVM-Light framework from my own button which is tied to a "close window command". This Command calls the static ViewModelLocator.Cleanup(), which calls an instance Cleanup() method on my MainWindowViewModel instance. The instance Cleanup() method then sets the property to which the MainWindow binds its DataContext, to null. The setter on the property raises a PropertyChanged event. Curiously, setting this property to null does not cause the window to close.
I am trying to understand why this is the case? If I set the MainWindow's DataContext to null, should that not be the same as Window.Close()? In my case, the Window and all of its elements remain on the screen. However, if I attempt further actions, I get null pointer exceptions, indicating the DataContext binding Property has indeed been set to null; this has also been confirmed in the debugger.
I have created a workaround by hooking the Application.Exit event and issuing a Window.Close() in the event handler in order to create my own "Close Window" button (ie, to create same functionality for my own Button / Command as clicking the X button in the upper right of a Window). Since calling a UI element (ie, the Window instance) from MVVM directly is not MVVM friendly, I used a ViewService to implement the Window.Close() functionality in order to keep the workaround MVVM friendly. I am a big fan of the ViewService idiom (or pattern), but I just don't think it should be necessary here; except, I could see how exiting the app is a special case that perhaps should tie-in with the application lifecycle, and .Net seems to only allow exiting a WPF app by issuing the Window.Close() method.
Thoughts appreciated.
I believe I have found the answer to my original question, in addition to the one raised in my comments discussion with flq.
First, the answer to the original question is that the proper way to close the Window is along the lines of what I did in my described "workaround". Closing an app is a View-initiated process, as it is the Window control that has the bits for how to do it. You can of course hook the Application.Exit event so that you can perform cleanup on your ViewModels, prompt the user to save data, etc..
The question raised by me after some interesting discussion with flq is, if I don't just set a control's DataContext (ie, ViewModel) to null in order to release the View and ViewModel resources, how should I do it?
An interesting discussion with some nuances can be found here, but the basic answer is that you find the parent control and remove the control you want to close from its Children list. Note, this is a different technique with a different goal than just making the control not visible by setting is Visibility property to Collapsed. In the following example, "this" is the control to be removed (ie, "Closed"):
Panel p = (Panel) this.Parent;
p.Children.Remove(this);
I am not sure if you still need to then set the child (ie, "this") to null to re-claim its resources, or, if just removing it from the visual tree will cause WPF to re-claim the resources; the above linked discussion makes no mention. As mentioned in the original discussion, the above technique can be supplemented by hooking it to certain events, or using other application specific logic.
I am using Surface Toolkit for Windows Touch Beta. I have a UserControl within a ScatterViewItem on a ScatterView. I want to receive ManipulationCompleted event on a UserControl but it doesn't seem to ever be raised even though IsManipulationEnabled="True" is also set. The same thing works perfectly in a non-Surface WPF4 app.
It appears various Touch WPF events play well with Surface but it seems like a lot of work to recreate a tap event and NSWE events that I can easily interpret from ManipulationCompleted event.
I am looking on ways to either receive ManipulationCompleted event on my UserControl or to simulate it by handling existing touch events.
Any pointers?
does the scatterviewitem move when your usercontrol is touched? only one element at a time can be tracking manipulations for a given touch. if the scatterviewitem is getting the manipulation events, that means your user control will not.
if you only want your usercontrol to handle the input, then have it listen to TouchDown and call usercontrol.Capture(touch). if you want to have the SVI do it's thing but also handled the completed event on your own, then you will have to register your event handler manually: usercontrol.AddHandler( ManipulationCompletedEvent, yourHandler, true). the last parameter says you want to handle the event even if SVI already has.
I have an application that, due to OpenGL airspace issues, has to host several controls in separate, exclusive windows. This is working quite well, but I am setting all of the windows to TopMost = true, which means that they do stay showing even when they lose focus, but they also overlay other applications. Also, it kind of binds me to using only one window at a time for this. Activate() doesn't work either.
I found that setting the windows' owners to the main app window allowed them to always float on top.
Inside the control that mediates the content and measurement of the child window:
InnerWindow.Owner = Window.GetWindow(this);
this being the windowHostControl hosting this window.
I use to combine Activate() and Focus() methods to show a hidden Window. Can you try using Focus() and let us know if this is working ?
I found some of my winform application controls, such as DataGridView and ToolStrips, are referred to by UserPreferenceChangedEventHandlers. I have no idea what setting of the controls will generate such references and why such references keep my control alive in memory. How can I remove such references from that event? Thanks.
It is the delegate type for the SystemEvents.UserPreferenceChanged event. This event fires when Windows broadcasts the WM_SETTINGCHANGE message. Which typically happens when the user uses a control panel applet and changes a system setting.
Several controls register an event handler for this event, DataGridView, DateTimePicker, MonthCalendar, ProgressBar, PropertyGrid, RichTextBox, ToolStrip, NumericUpDown. They typically are interested in font or cue changes and anything that would affect the layout.
SystemEvents.UserPreferenceChanged is a static event. Registering a handler and forgetting to unregister it causes a memory leak, it prevents the control from being garbage collected. The listed controls ensure this doesn't happen, they unregister the event handler in either the OnHandleDestroyed() or the Dispose() method.
You'll get in trouble when neither of those two methods run. That will happen when you remove the control from the container's Controls collection and forget to Dispose() it. While forgetting to call Dispose() is not normally a problem, it is a hard requirement for controls. It is easy to forget too, controls are normally automatically disposed by the Form. But that only happens for controls in the Controls collection.
Also be sure to call Dispose() on forms that you display with the ShowDialog() method, after you obtained the dialog results. The using statement is the best way to handle that.
One more excruciating detail is important about the UserPreferenceChanged event, it is often the one that deadlocks your app when you create controls on a worker thread. Typically when the workstation is locked (press Win+L). Which cannot come to a good end when you use the controls I listed, the SystemEvents class tries to raise the event on the UI thread but of course cannot do this correctly when more than one thread has created them.
Also the kind of bug that can have a lasting effect, a splash screen for example can get the SystemEvents class to guess wrong about which thread is your UI thread. After which it then permanently raises the event on the wrong thread. Very ugly to diagnose, the deadlock is well hidden.
i would like to create a simple winforms or wpf application where i can drag and drop virtual "cards". this below is not exactly what i want to do, but it the closest thing that i found on the web to represent the user interface.
http://www.greenpeppersoftware.com/confluence/plugins/advanced/gallery-slideshow.action?imageNumber=1&pageId=24870977&decorator=popup&galleryTitle=Task+board+and+transitions
so basically i want to have columns in the GUI where i can drag and drag from one to the other.
My questions are:
would this be easier in winforms or wpf
where do i start?
In both winForms and WPF dragging and dropping can be done in a similar way by working with the events on the target DragOver and Drop.
However with WPF you have other options. You will also be able to make the application look better by having a thumbnail as you drag (this is possible in winforms but harder to achieve).
Have a look at this WPF sample it uses a helper class and think it does exactly what you need.
I agree with John in that WinForms and WPF are quite close to one another w.r.t. drag'n'drop. But WPF offers more of a "common base" for ItemsControl, allowing to implement more independent of the final UI elements used (ListBox, ListView, TreeView... can be easily switched). And obviously WPF allows much more fancy effects.
I would strongly recommend this blog post:
http://www.beacosta.com/blog/?p=53
both for some drag'n'drop basics and for a clean WPF drag'n'drop approach.
It shows a nice implementation of a rather generic helper for drag'n'drop from/to WPF ItemsControls, I really like that "Insertion Adorner". And I do like that the drag'n'drop code is nicely separated from the user control itself by using attached properties, which makes it much easier to use and maintain.
It would probably be slightly easier in WPF because of the Thumb control which provides easy to use built-in support for dragging. (If I remember correctly, in WinForms you would need to handle the mouse events yourself, whereas the WPF Thumb does this for you and translates them into drag start, delta and end events.)
However if you are much more familiar with one framework than the other than that would probably dwarf the difference that the Thumb control would make.
You should also have a look around for toolkits/frameworks that could handle this for you -- I think they exist for both WinForms and WPF (not sure though).
A good way for darg and drop are explained as
Detect a drag as a combinatination of MouseMove and MouseLeftButtonDown
Find the data you want to drag and create a DataObject that contains the format, the data and the allowed effects.
Initiate the dragging by calling DoDragDrop()
Set the AllowDrop property to True on the elements you want to allow dropping.
Register a handler to the DragEnter event to detect a dragging over the drop location. Check the format and the data by calling GetDataPresent() on the event args. If the data can be dropped, set the Effect property on the event args to display the appropriate mouse cursor.
When the user releases the mouse button the DragDrop event is called. Get the data by calling the GetData() method on the Data object provided in the event args.
You can find the complete article here