Creating a custom Stackpanel with Transparent "Windows" - wpf

Good evening. I have been researching my issue for about two days now, and have tried at least a dozen variations of the problem. Every single last one of them has been a complete total failure, so I won't bother posting any code here.
What I need is a StackPanel for a Border-less Window. The StackPanel needs to have an opaque background, and the children need to have a transparent background. I need the transparency to carry ALL the way through the child element, StackPanel, and underlying main window.
Does anyone have any idea as to how to go about this, and where I should start? I am an intermediate level programmer, and have hit a solid wall here. I have tried to inherit from Panel Class, over-riding the usual suspects; MeasureOverride, ArangeOveride, OnRender, etc..
One of the thoughts I also had, was using some form or another of CombinedGeometry between the parent, and all child nodes.
Thanks in advance!!

What you are probably missing is Window.AllowsTransparency="True". Sad to say that you have to build your own window title and border because WindowStyle property must be set to None.
I've created a sample for you:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
AllowsTransparency="True"
Background="Transparent"
WindowStyle="None"
WindowStartupLocation="CenterScreen">
<Border BorderBrush="Black" BorderThickness="1">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Application">
<MenuItem Header="_Close" Click="MenuItem_Click"/>
</MenuItem>
</Menu>
<ToolBar DockPanel.Dock="Bottom">
<Button>sample button</Button>
</ToolBar>
<StackPanel>
... your whiteboard ...
</StackPanel>
</DockPanel>
</Border>
</Window>
.
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
this.Close();
}

Ok, so after many, MANY, hours of brute force "hacking," I have managed to figure out a means to accomplish what I need.
<Border Name="OuterBorder" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderBrush="Orange" BorderThickness="1.5">
<Border Name="FillerStrip" BorderBrush="Black" BorderThickness="20">
<Border Name="InnerBorder" BorderBrush="Orange" BorderThickness="1.5">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent">
<local:GlassBoardControl BorderBrush="Orange" BorderThickness="2" AxisBrush="LawnGreen" AxisThickness="15" x:Name="screen" />
</StackPanel>
</Border>
</Border>
</Border>
Now my only question is how on earth do I go about creating a custom panel with this as it's border? Any ideas as to where I should start would be much appreciated.

Related

Please teach me how to use ControlTemplate properly?

Ok, I think I am pretty noob on using ControlTemplate lol...
All I want is to template my every UserControl so every UserControl will have Delete, Save, Cancel buttons.
So now I am writting some test......
Try to make a border around the everything...
In App.xaml
<Application.Resources>
<ControlTemplate x:Key="DeleteSaveCancelTemplate">
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Border BorderThickness="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Black">
<ContentPresenter/>
</Border>
</Grid>
</ControlTemplate>
</Application.Resources>
Then in UserControl:
<UserControl ... Height="150" Width="300" Template="{StaticResource DeleteSaveCancelTemplate}" >
But all I see is just ...black... what did I do wrong? I thought I should use ContentPresenter to shows the content?
For most controls you use ContentPresenter. ItemsPresenter only applies to elements that extend ItemsControl.
So... I found the problem lol From this page
The ControlPresenter doens't bind to Content by default...
I need to write
<ContentPresenter Content="{TemplateBinding Property=ContentControl.Content}" />
Unless you have specify the TargetType, then you it will bind by default.

Prevent WPF control from expanding beyond viewable area

I have an ItemsControl in my user control with a scroll viewer around it for when it gets too big (Too big being content is larger than the viewable area of the UserControl). The problem is that the grid that it is all in just keeps expanding so that the scroll viewer never kicks in (unless I specify an exact height for the grid). See code below and thanks in advance.
<UserControl x:Class="BusinessObjectCreationWizard.View.TableSelectionPageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<GroupBox FontWeight="Bold" Height="300px"
Header="Tables"
Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal"
ItemsSource="{Binding Path=AvailableTables}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Path=DisplayName}"
IsChecked="{Binding Path=IsSelected}"
Margin="2,3.5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</UserControl>
This user control is loaded here
<Border Background="White" Grid.Column="1" Grid.Row="0">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"
Header="{Binding Path=CurrentPage.DisplayName}" />
</Border>
I would like to not specify the height.
If you remove the Height from your GroupBox (which, as far as I understand, is what you want to do), then it will fill its container, unless there's a panel upstream that imposes its own sizing rules.
I used this simplified version of your XAML. I removed the template and the binding, and hard-coded some items, to make this stand alone; those changes won't affect the way layout is done.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<GroupBox FontWeight="Bold" Header="Tables" Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal">
<TextBlock>Foo</TextBlock>
<TextBlock>Bar</TextBlock>
<TextBlock>Baz</TextBlock>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</Window>
Run it, and you'll see that the content does indeed size to fit the window, and the scrollbar only enables when the window gets too small to see all three items. I believe this is what you want.
So the problem is most likely one of the parent panels, one you're not showing in your sample XAML. The problem you describe could occur if your GroupBox appears inside a StackPanel:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<StackPanel>
<GroupBox FontWeight="Bold" Header="Tables" Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal">
<TextBlock>Foo</TextBlock>
<TextBlock>Bar</TextBlock>
<TextBlock>Baz</TextBlock>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Window>
Now the GroupBox appears at the top of the Window, sized to exactly fit its contents. If you shrink the Window enough, the GroupBox will be cut off -- because it's sized to fit its content, not its container. This sounds like the problem you're describing.
The reason is that StackPanel asks its children what their ideal height is (based on their content), and uses that height. Without StackPanel (or something similar), the default is to respect the control's VerticalAlignment, and if that's set to the default value of Stretch, then the control is stretched to fill its parent. This means it won't be taller than its parent, which sounds like what you want.
Solution: remove the StackPanel (or whatever else is causing you problems) and use something else. Depending on what you're trying to accomplish, you might have better luck with a DockPanel or a Grid. Hard to tell without knowing more about your layout.
Edit: Okay, it looks like the problem is indeed the HeaderedContentControl parent -- but not directly. HeaderedContentControl isn't a panel, so it doesn't do any layout of its own (and its descendant, GroupBox, doesn't have this same problem). The problem is its default template -- which includes a StackPanel. The good news is, you're free to use a different template, let's say one with a DockPanel instead:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<HeaderedContentControl>
<HeaderedContentControl.Style>
<Style TargetType="{x:Type HeaderedContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedContentControl}">
<DockPanel>
<ContentPresenter ContentSource="Header" DockPanel.Dock="Top"/>
<ContentPresenter/>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</HeaderedContentControl.Style>
<GroupBox FontWeight="Bold" Header="Tables" Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal">
<TextBlock>Foo</TextBlock>
<TextBlock>Bar</TextBlock>
<TextBlock>Baz</TextBlock>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</HeaderedContentControl>
</Window>
If you leave off the <HeaderedContentControl.Style> part, this reproduces your problem; but with the style in place, it allows the GroupBox to fill its container, so the ScrollViewer will get a scrollbar when you want it to.
If the previous answer doesn't fix the problem, you could also try binding the Width, Height of your grid to the ActualWidth, ActualHeight of your parent UserControl. Something like:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication.UserControl1"
x:Name="UserControl">
<Grid Height="{Binding ElementName=UserControl, Path=ActualHeight}"
Width="{Binding ElementName=UserControl, Path=ActualWidth}" />
In this case you aren't setting an explicit width and height but you are limiting the Grids width/height to the constraints of the UserControl it sits in.
I had the same issue, after reading this response I replaced all StackPanels with Grids in UserControl. It resolved the Scrollbar issue.
Try removing the grid entirely and setting the HorizontalAlignment and VerticalAlignment directly on the GroupBox. If a layoutpanel has only one child, it's often redundant... this migth be true in your case.
If that doesn't work... what's the parent of your grid control?
Why not just use a listbox instead of an itemscontrol, that has a built in scrollviewer.
They are different. If you do not want to have the items selectable, then don't use a ListBox. It is going to be heavier, and will also have the deselect a selection everytime the user clicks on an entry. Just put the ItemsControl in a ScrollViewer
I had the same problema with ListBox, it wasn't expanding and the scroll viewer didn't appear. I solved it as follows:
<UserControl x:Class="TesteView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid MaxHeight="710">
....
....
<StackPanel>
<ListBox MaxHeight="515"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding Path=Teste,Mode=TwoWay}">
....
....
</ListBox>
</StackPanel>
</Grid>
</UserControl>

Get the scroll position from a RichTextBox?

I've created a highlighting mechanism for a RichTextBox in Silverlight 4. It'll get character positions and draw rectangle(s) over the text.
The trouble I have now is with scrolling on the RichTextBox. As I scroll all of my precious highlighting gets left behind. Is there any way I can add an event handler to a scroll event and/or a scrolling position of the RichTextBox? Or is there some better way in which I can link the position of the highlighting rectangles to the RichTextBox?
The trick would be to get what ever panel (I guess its a Canvas?) that you are overlaying the RichTextBox with to actually exist within the same ScrollViewer that rich text exists in.
The following is very rough idea but should get you on the path to reasonable solution.
You can do this using a custom style for the RichTextBox. The default style for this control can be found here.
Copy this style into a resource in your containing UserControl and point your RichTextBox Style property at it. So far nothing is different but now you can play about with the template. The relevant portion currently looks like this:-
<Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
<ScrollViewer x:Name="ContentElement" Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False" />
</Border>
Now we can tweak it like this:-
<Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
<ScrollViewer Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False">
<Grid>
<ContentControl x:Name="ContentElement" />
<Canvas x:Name="HighlightOverlay" />
</Grid>
</ScrollViewer>
</Border>
You'll note that we've moved the name "ContentElement" from the ScrollViewer to the new ContentControl. Having a FrameworkElement called "ContentElement" is the only feature that the RichTextBox stipulates about its template.
Now overlaying this ContentControl we can place a Canvas where you can place your highlighting rectangles. If the user scrolls this RichTextBox the whole Grid containing both the Content and the Highlights will scroll together.
The only remaining trick is acquiring the "HighlightOverlay" so that you can add your rectangle to it. Here is some code that will grab it:-
private Canvas HightlightOverlay;
public MyUserControl()
{
InitializeComponent();
MyRichText.LayoutUpdated += MyRichText_LayoutUpdated;
}
void MyRichText_LayoutUpdated(object sender, EventArgs e)
{
HightlightOverlay = MyRichText.Descendents()
.OfType<Canvas>()
.FirstOrDefault(elem => elem.Name == "HighlightOverlay");
}
You will be wondering where the Descendents method is coming from, it is here.
Anthony W Jones came up with a brilliant solution. There were just a couple tweaks to the XAML I had to make.
As suggested I started with this inside the template:
<Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent">
<ScrollViewer Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False">
<Grid>
<ContentControl x:Name="ContentElement" />
<Canvas x:Name="HighlightOverlay" />
</Grid>
</ScrollViewer>
</Border>
But the ContentControl messed things up somehow and you can't actually type into the RichTextBox anymore. Also, the scroll bars weren't showing up.
But I found the two changes necessary to make this work:
<Border x:Name="MouseOverBorder" BorderBrush="Transparent" BorderThickness="1">
<ScrollViewer BorderThickness="0" IsTabStop="False" Padding="{TemplateBinding Padding}" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid>
<Grid x:Name="ContentElement" />
<Canvas x:Name="HighlightOverlay" />
</Grid>
</ScrollViewer>
</Border>
Adding HorizontalScrollBarVisibility="Auto" and VerticalScrollBarVisibility="Auto" brought the scroll bars back, and simply using Grid instead of the ContentControl made the RichTextBox editable again.

Double Border with a VisualBrush in WPF

I'm curious if anyone knows of a way to easily get a double border effect in WPF similar to what you see in the selected items in Windows Explorer in Windows 7.
If you look closely, you'll notice that the selected item has a dark border, a lighter, inner-border, and then a gradient background.
Currently, I'm using two borders around an object any time I want to achieve this effect. Doing it that way is ugly syntactically and really muddies my view xaml. Being a web developer at heart I'd like to separate the xaml structure from the style as much as possible. So, I've started putting this into Styles and Content Templates in order to pull it out of my view xaml.
However, I'm curious if there may be a better way to go about this.
I played around for a while using a VisualBrush as a background to try to get the effect. However, I want to be able to apply the background to elements that can be any size and the way the VisualBrush stretched the visual to fit the element background didn't work the way I wanted it to. Essentially, I'd really just like it to stretch the visual the way the WPF layout system would do it.
Any ideas would be greatly appreciated.
--
Dusty
A VisualBrush is probably not what you want to do in this scenario, as it's pretty heavy.
You can solve the problem with some Xaml without nesting borders.
For example,
<Border BorderBrush="#FF00B5C5" BorderThickness="1" CornerRadius="2" Background="White">
<Grid Background="#FF00B5C5" Margin="1">
<Rectangle Fill="#FFA2F2FE" />
<TextBlock Text="This is some text" VerticalAlignment="Center"/>
</Grid>
</Border>
You can, of course, tweak the properties to get the look you need.
EDIT: If you want to create a style, so you can reskin the look-and-feel, you can do something like this:
<Window.Resources>
<Style x:Key="BorderedTextBlock" TargetType="ContentControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="#FF00B5C5" BorderThickness="1" CornerRadius="2" Background="White">
<Grid Background="#FF00B5C5" Margin="1">
<Rectangle Fill="#FFA2F2FE" />
<TextBlock Text="{Binding}" VerticalAlignment="Center"/>
</Grid>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ContentControl Style="{StaticResource BorderedTextBlock}" Content="This is some text" Width="200" Height="24"/>
</Grid>
Additionally, turn this into a custom control with all the styling and theming parameters that you need.
Hope that helps,
Sergio

Making a control "transparent" to click events

I have a ListBox displaying some items, and in certain modes I "stamp" a kind of watermark across the top of it. I've done this with a Border containing a TextBlock with an Opacity of 0.5. All this works nicely.
However, I still want the user to be able to click on the items in the ListBox but if I click on the "stamp" it obviously eats the click events and they're not seen by the ListBox.
What do I have to do to prevent this? (i.e. allow the ListBox to see the Click event)
Thanks,
Craig
You can do this with the IsHitTestVisible property:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ListBox>
<ListBoxItem>a</ListBoxItem>
<ListBoxItem>b</ListBoxItem>
<ListBoxItem>c</ListBoxItem>
</ListBox>
<Border Opacity="0.2" Background="Cyan" BorderBrush="Black" BorderThickness="5" IsHitTestVisible="False" >
<TextBlock Text="EXAMPLE" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>

Resources