Docking with ElementHost - wpf

I am attempting to add a WPF usercontrol to an existing WinForms project and get the WPF UserControl to dock and fill the entire space.
There's a current framework that loads WinForms UserControls into a parent form (into a panel) in response to button clicks. This is where I'm trying to hook in - The WinForms UserControl that's currently getting loaded will have the ElementHost.
Hierarchy:
Form1.cs - contains a panel that gets WinForms UserControls dynamically loaded
WinForms UserControl - contains the ElementHost
WPF UserControl
The ElementHost has Dock set to Fill and its Child property set to ucReport, which is a WPF UserControl, which has the following markup (only top level design included):
<UserControl x:Class="MyClassName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TabControl HorizontalAlignment="Left" Name="tabControl1">
<TabItem Header="Header1">
...The interesting stuff goes here
</TabItem>
</TabControl>
</UserControl>
The content of the UserControl does expand vertically when I resize the form, but horizontally, the content only expands large enough to accomodate its content.
When I view the WinForms UserControl (the one that has the ElementHost) in the designer, the problem is apparent. The WPF content that's specified is getting rendered and it's filled top to bottom, but not left to right.
I'm of the mind that it's something in the XAML that has to be set (perhaps on the UserControl declaration?) to get it to fill it's parent container, which is the ElementHost - I just can't find the property.
Would someone enlighten me?

Change HorizontalAlignment to Stretch or get rid of it entirely.

Related

Scroll WinForms DataGridView in a WPF WindowsFormsHost

I have Scrollviewer which contains a frame with a WindowsFormsHost. The WindowsFormsHost contains a DataGridView (please don't ask why I'm not doing this with a WPF DataGrid Control).
Because the DataGridView causes display errors while scrolling with the scrollviewer I disabled the scrollviewer and enabled the scrolling on my DataGridView.
<Grid x:Name="LayoutRoot">
<WindowsFormsHost HorizontalAlignment="Stretch" Name="_windowsFormsHostGrid" VerticalAlignment="Stretch">
<Win.Grid:DataGridView x:Name="_buchungGrid" ScrollBars="Both" BorderStyle="None" BackgroundColor="#F7F8FA" CellFormatting="_gridBuchungen_CellFormatting" SelectionChanged="GridSelectionChanged" DoubleClick="_buchungInovaGrid_DoubleClick" AutoSize="True" AutoColumnWidthMode="Window" ZebraColor="LightGray" Anchor="Left" Dock="Fill" />
</WindowsFormsHost>
</Grid>
This seems to work. As long I don't resize the Window. When I resize the window (and this will cause all child elements to resize including scrollviewer, frame and WindowsFormsHost), the scrollbars of my DataGridView disappears and I'm not longer able to scroll my grid. I can resize to the old size of the window, but the scrollbars are still hidden.
Any idea why this happens and how to fix it? I'm also not sure why they disappear because I'm resizing just one pixel and this occurs.
There seems not to be a solution for this problem. I ended up by using a WPF DataGrid and extend it by the functionality I need.
WinForms will be painted all the time at the top of all other elements. The only solution seems to wrap a windows around it to get scrolling fixed but this would ugly as hell.
Given a Forms.DataGridView (dgv) inside a Forms.UserControl (myUserControl) inside a WindowsFormsHost, I discovered that the DGV was given larger dimensions than the UC, so the scrollbars were not visible. (If UC is instead in a WinForm, scrollbars appear as expected; there seems to be an issue with resize logic inside WFHost.)
I was able to fix this in my UC's SizeChanged handler:
// VB code:
Public Class MyUserControl
...
Private Sub MyUserControl_SizeChanged(sender As Object, e As EventArgs) Handles MyBase.SizeChanged
' dgv.Size is much larger than it should be; not sure why.
' my dgv has controls above it, but extends all the way to bottom and right;
' if yours does not, then subtract more as needed.
dgv.Size = New Size(Me.Size.Width - dgv.Left, Me.Size.Height - dgv.Top)
End Sub
...
End Class
The result is that the WinForms drawing stays within the area it is supposed to; the scrolling is done in WinForms. (NOT using a WPF scrollviewer.)
XAML for WPF:
...
xmlns:mywf="clr-namespace:MyWinFormAssembly;assembly=MyWinFormAssembly"
...
<WindowsFormsHost>
<mywf:MyUserControl />
</WindowsFormsHost>

Linear navigation in WPF MVVM

I'm using the Cinch MVVM framework, however I think this is something that relates to all WPF approaches.
I want to have a primary screen - Shell or MainWindow - which then contains various viewmodels. To navigate between viewmodels I'm using (or going to use) a tab control styled as a button strip with the content area beneath - which is all ok as I add the viewmodels to the tabcontrol (well to the 'Views' collection which is bound to the tab control) at runtime.
A screen that doesn't fit into this methodology is the sign in screen, which I don't really want to add to the tab control - preferably it should be in it's own usercontrol which takes up the entire screen apart from covering the logo; that is, I would like it to appear in the same window rather than a popup dialog, however I'm not sure how to add/ remove controls dynamically and then add subsequent tabcontrol once the user has signed in successfully (and remove the sign in screen). What containers should be used?
TIA.
The easiest way is put your tabcontrol in a Grid without columns and rows so it fill the grid by default. Then add an extra grid or loginusercontrol to the grid as shown below. The moment a user needs to login you can set the visibility of the MainTabControl" to collapsed and of the LoginGrid to Visible and switch it back after a succesfull login. I hope that the xaml below will help you enough.
<Grid>
<TabControl x:Name="MainTabControl" Visiblity="Visible">
... put your tabs here ...
</TabControl>
<Grid x:Name="LoginGrid" Background="#60FFFFFF" Visibility="Collapsed">
... put your usercontrol to login here or the login controls themself
</Grid>
</Grid>
You could use a ContentControl with content bound to a view model. So you'd have two view-models representing the sign-in screen and the main screen and use DataTemplate to display appropriate screen.
<Window.Resources>
...
<DataTemplate DataType="{x:Type my_view_models:SignInViewModel}">
<my_controls:SignInScreenView />
</DataTemplate>
...
</Window.Resources>
<ContentControl Content={Binding} />
You may be interested by Lakana, it is a lightweight and non intrusive navigation framework for WPF.
Riana

convert wpf textbox to iwin32window (winform)

As the title describes, I want to convert System.Windows.Controls.Textbox to IWin32Window.
I read How to use a FolderBrowserDialog from a WPF application
but it only describes how to get handle of winform, not control on it.
Thanks
WPF does not use Win32 handles for individual controls like TextBox, only for the Window itself. In other words, from Win32's perspective the entire WPF Window object is a single window with single handle.
Because of this, it is meaningless for an IWin32Window to return the "actual" Win32 handle of a WPF TextBox: A WPF TextBox simply doesn't have a Win32 handles. Thus you will have to return a Win32 handle of some other object.
How to do this depends on what you will be using the IWin32Window for. There are several possibilities for creating a Win32 window to correspond to your TextBox:
You could create a transparent Win32 window that overlaps the TextBox (useful for hit-testing or overdrawing scenarios)
You could create a zero-size window that is centered on the TextBox (useful for dialog initial-location and ownership scenarios)
You could host the TextBox in an ElementHost rather than in a WPF Window (useful if you want the TextBox to fit in with other Win32 stuff, such as an old MFC application)
You could host the TextBox in an ElementHost inside a WindowsFormsHost (useful if you need a Win32 window around the TextBox but still need WPF layout
Notes on the "extra window" solutions (1 & 2)
To create a Win32 window that overlays the TextBox (either transparent or zero-size), you would use traditional Win32 or WinForms techniques.
Since the TextBox can move on the screen you would need to move the Win32 window whenever the TextBox moves. This can be done in the OnRendering event using textBox.TransformToAncestor(window) then transforming to device coordinates using PresentationSource.TransformToDevice.
Notes on ElementHost solutions (3 & 4)
This is as simple as wrapping the ElementHost around the TextBox in your XAML, so this:
<Grid>
...
<TextBox ...>
</Grid>
might become:
<Grid>
...
<WindowsFormsHost>
<ElementHost>
<TextBox ...>
</ElementHost>
</WindowsFormsHost>
</Grid>
This can also be done in code by removing the TextBox from its parent, adding it to a newly-created ElementHost, and then adding the ElementHost to a newly-created WindowsFormsHost and adding the WindowsFormsHost back to the parent.
Note that WPF styles and properties (including DataContext, TextElement properties, etc) do not propagate down through ElementHost, even if wrapped inside a WindowsFormsHost, so the desired settings and resources must be propagated manually.

Best practice for dynamically adding controls through copying existent controls in WPF

I have a TabControl.
While clicking a button I want to add a new TabItems to the TabControl.
Among various techniques I've chosen to create UserControl inside of a DockPanel.
Everything works fine, except for the location of control on a new TabItem is not 0 and it is
not expanded on the main form resize.
Everything is quite simple.
<TabItem Header="new control">
<DockPanel LastChildFill="True">
<tc:TabItemTemplate/>
</DockPanel>
</TabItem>
And TabItemTemplate is also located inside a DockPanel, but it's not docked inside a TabItem. What's the catch?
I would suggest using TabControl.ItemTemplate, and bind TabControl.ItemsSource to ObservableCollection of ViewModels. When user clicks on a button, add new View Model instance to the collection. Refer to this article for more details: WPF Apps With The Model-View-ViewModel Design Pattern

Remove focus rectangle on a UserControl

I have a WPF UserControl with Focusable="True". It's the only focusable control in the window.
Whenever the user presses Tab or Alt (and especially when they Alt+Tab to another application), my UserControl acquires a dotted-line border, aka focus rectangle. The focus rectangle then stays there until the window is closed.
How can I prevent my UserControl from ever displaying this focus rectangle?
Edit
It turns out the focus rectangle wasn't actually being displayed by my UserControl. My Focusable UserControl contained another UserControl that, in turn, contained an ItemsControl, and the ItemsControl is what was showing the focus rectangle.
When I added FocusVisualStyle="{x:Null}" to the ItemsControl, the focus rectangle went away.
If you want to not display the focus rectangle in any case you could set the FocusVisualStyle to null.
<MyControl FocusVisualStyle="{x:Null}" />

Resources