WPF TabControl with WindowsFormsHost unknown border issue - wpf

I am writing a text editor in WPF and I seem to have a strange border that I can't remove.
I've got a grid with a tabcontrol, and when a user selects "File -> New", I programatically add a new tabitem to the tabcontrol. I'm setting the tabitem content to an instance of WindowsFormsHost in order to host the ScintillaNet WinForms control.
Here's the problem: http://i.stack.imgur.com/kotSb.png
I'm pretty sure the border is not coming from the WinForms control itself, as I've used it elsewhere in the same configuration and it has no border.
The red border you see is added by me to highlight the problem (in the method that is responding to File -> New), with the following code:
tabControl.BorderThickness = new Thickness(3, 3, 3, 3);
tabControl.BorderBrush = Brushes.Red;
tabControl.Items.Add(tab);
tab.Focus();
Any ideas where this inner grey border is coming from?

That is the Padding on the TabControl, which is a margin it applies to the hosted child element. See Alignment, Margins, and Padding Overview. It is set to 4 in the default TabControl style. Try setting the Padding to zero explicitly:
tabControl.BorderThickness = new Thickness(3, 3, 3, 3);
tabControl.BorderBrush = Brushes.Red;
tabControl.Padding = new Thickness(0);
tabControl.Items.Add(tab);
tab.Focus();

Related

How to get a one-to-one match of an Adorner with its adorned element within a ScrollViewer?

wpf
I have a control A, (an inkcanvas), within a Grid within a ScrollViewer. Conrol A is taller then the physical window, so the ScrollViewer correctly adds a vertical scroll bar and the entire control can be viewed by scrolling down. When attaching an Adorner with a control B, (another inkcanvas), to control A, scrolling downward shows the Adorner to be cut off at the bottom of the screen. That is, the Adorner is not completely covering the adorned element and/or is not extended downward when scrolling.
How do I get the Adorner (the control) to completely cover the adorned element and respect the ScrollViewer. (I need a one-to-one match between the pixels of the Adorner control and the adorned element within the ScrollViewer).
TIA
Edit#1: The key line in the Adorner that sets the background of the InkCanvas is
_inkcanvas.Background = CreateGrid();
public InkCanvasTextAdorner(InkCanvas element)
: base(element)
{
_element = element;
_visuals = new VisualCollection(this);
_inkcanvas = new InkCanvas();
_inkcanvas.Background = CreateGrid();
_visuals.Add(_inkcanvas);
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(element);
adornerLayer.Add(this);
}
If an InkCanvas is used (as the above) the Adorner's background is clipped at the bottom. The adorner control, however, does continue to the bottom of the adorned element.
However, if a Canvas is used instead of the InkCanvas, the Adorner's background does extend to the bottom of the adorned element.
What's wrong?
I'm guessing that the difference in the Background property expanding past the physical screen with the Canvas and not the InkCanvas may be because the Canvas inherits from Panel whereas the InkCanvas does not. Based on the finding that the Canvas background does do what I need, I find the below code does accomplish what I want--the canvas allows images from layers beneath it to be seen yet posts a grid of lines overwhich the InkCanvas will accept strokes. All is well :)
public InkCanvasTextAdorner(InkCanvas element)
: base(element)
{
_element = element;
// The VisualCollection has only one visual parent. I.e. InkCanvasTextAdorner is the parent to the VisualCollection.
// By overriding default rendering behavior of the VisualCollection, any kind of control and its children can be placed in the Adorner.
_visuals = new VisualCollection(this);
_inkcanvas = new InkCanvas();
_inkcanvas.Background = Brushes.Transparent;
_canvas = new Canvas();
_canvas.Background = CreateGrid();
_grid = new Grid();
_grid.Children.Add(_canvas);
_grid.Children.Add(_inkcanvas);
// The _grid is a logical child of the VisualCollection of the Adorner. The ArrangeOverride and MeasureOverride will set up the Grid control.
_visuals.Add(_grid); // Adding a single control for display.
// AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(element);
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(element);
adornerLayer.Add(this);
}
Which looks like:

How to add borders to WPF ListView in code

I have created a ListView with a GridView in code.
ListView gridList = new ListView();
GridView gridListView = new GridView ();
gridList.View = gridListView;
Now, I define a GridViewColumn, set the header, width and bindingPath. All good and the data shows up.
GridViewColumn listColumn = new GridViewColumn();
listColumn.Header = "Some Header";
listColumn.Width = 100.0;
listColumn.DisplayMemeberBinding = new Binding("Name");
gridListView.Columns.Add(listColumn);
But there are no borders/gridlines shown on display of this ListView. How can I add borders through code?
Someone described my exact problem here but no good solution mentioned
http://social.msdn.microsoft.com/Forums/en-US/fa4fa8e0-81fe-487a-8763-590062d29c06/wpf-listview-gridview-row-border?forum=wpf
The logic in WPF programming is totally different from what you've done in winforms. Everything related to UI should always be set up using XAML (as much as possible). The WPF library itself has many parts desgined mainly for use in XAML although there is always an equivalent codebehind. However that's when using codebehind may be awkward and non-intuitive (as well as straight-forward).
I understand that you want something like the ListView Grid in Winforms. In WPF that can be achieved easily if you use XAML code. Even in code behind, you can always build a Style or Template from XAML string (with the help of XamlReader). This approach is good for complex scenario but in this case I have another approach (don't use the XAML parser at all). This trick does render the grid which is good enough (and at best it can do for the trade-off of simplicity):
//we need an instance of Style to set to ListView.ItemContainerStyle
var style = new Style(typeof(ListViewItem));
//set the bottom border thickness to 1
var setter = new Setter(Control.BorderThickness, new Thickness(0,0,0,1));
style.Setters.Add(setter);
//set the border brush
var borderBrush = new LinearGradientBrush { StartPoint = new Point(0,0),
EndPoint = new Point(1,0)};
var gradStop = new GradientStop(Colors.Transparent, 0.001);
borderBrush.GradientStops.Add(gradStop);
gradStop = new GradientStop(Colors.Green, 0.001);
borderBrush.GradientStops.Add(gradStop);
gradStop = new GradientStop(Colors.Green, 0.999);
borderBrush.GradientStops.Add(gradStop);
gradStop = new GradientStop(Colors.Transparent, 0.999);
borderBrush.GradientStops.Add(gradStop);
setter = new Setter(Control.BorderBrush, borderBrush);
style.Setters.Add(setter);
yourListView.ItemContainerStyle = style;
Note that the default inner Border of each ListViewItem has a hard-coded CornerRadius of about 2, so by setting just the bottom BorderBrush to a solid brush such as Brushes.Green will show a little upwards curly line at the 2 ends of the bottom border. You can try it yourself. If this result is acceptable, the code can be shorter and simpler (because you don't have to define the GradientBrush to cut-off the 2 curly ends) like this:
setter = new Setter(Control.BorderBrush, Brushes.Green);
style.Setters.Add(setter);
If the behavior is still not what you want. You should try the approach I mentioned about using XamlReader to parse a XAML string and get an instance of whatever you want in codebehind. (you can search it yourself, it's easy to have some result).
I suggest you see this link, it contains a dynamic GridView created in code-behind that can be useful for your specific case. For the code sample that you provided, you didn't add ShowGridLines property.

WPF - How to finding the tab a control is on

I'm very new to WPF and don't know how to do this. I have a text box in a tab item on a tab control. How can I programmatically (C#) determine what tab item is the parent of this text box? I would also like to determine what tab control is the parent of the tab item.
Thanks very much.
TabItem.Parent will provide the logical parent element of the TabItem; which will be the associated TabControl. You can use the same approach for any control with the TabItem.
((FrameworkElement)myTextBox.Parent).Parent;
If the item is deeper in the tree and becomes unknown in its depth you will need to begin to approach it in a recursive manner.
You can use FrameworkElement.Parent to walk up the hierarchy of a control in WPF. This should let you (recursively) walk up until you find the TabItem, then walk up to the TabControl from there.
I am newbie in WPF too, but what about cycle searching?
For example:
TextBox TB = new TextBox();
TabControl MyTabControl = new TabControl();
// ...
foreach (TabItem ti in MyTabControl.Items)
if (TB.Parent == ti)
{
// textbox is here!
MessageBox.Show(ti.ToString());
break;
}
Here is a generic method for finding parent controls: How can I find WPF controls by name or type?
You can call it like this:
TabItem owner = UIHelper.FindVisualParent<TabItem>(myTextBox);

HwndSource does not display the Adorner layer

When I host WPF controls that use adorners (the error border of the textbox) in a HwndSource the adorners are not shown. It seems that the adorner layer is not there. Why is that so and what can I do against it? Is this a known bug?
The AdornerLayer is usually part of the default template of the Window class.
If you are customizing the Window template or host controls in something else than a Window; you'll have to create the AdornerLayer yourself.
This can be done by wrapping your WPF controls inside a System.Windows.Documents.AdornerDecorator:
hwndSource.RootVisual = new AdornerDecorator { Child = yourTextBox };

How to dynamically embed a XAML/Codebehind pair as a child of a StackPanel?

I have a StackPanel called "MainContent".
I can dynamically fill it with UIElements like this:
TextBlock textBlock = new TextBlock();
textBlock.Text = String.Format("This is text");
textBlock.Background = new SolidColorBrush(Colors.Beige);
MainContent.Children.Add(textBlock);
Button button = new Button();
button.Content = "This is a button";
MainContent.Children.Add(button);
But I want to go beyond that and fill it with a XAML/Codebehind pair (e.g. Page or Window):
Type type = this.GetType();
Assembly assembly = type.Assembly;
Window window = (Window)assembly.CreateInstance(String.Format("{0}.{1}", type.Namespace, "Test1"));
MainContent.Children.Add(window);
But the above code complains that I can't add a "Window to a Visual". I can do window.ShowDialog() of course but then it is external to my main window.
I want the Test1 window to be embedded in my application.
How can I do this?
Added: The main question is: how can I get Window (or Page) to act as a UIElement so I can embed them dynamically in StackPanels, etc. Currently looking at XamlLoader, anyone experienced with that?
The easiest way is to have your XAML/code behind inherit from UserControl - and then everything will just work
I don't know if it's possible. But instead of filling the panel with a Window, why not fill it with the root element of the window instead. You'll get all the content and value of the window without the unneeded chrome.
Update: you can grab the root element of a window via the Dependency Property Content
Window w;
object rootElement = w.Content;
Have you tried this?
MainContent.Children.Add(window.Content);

Resources