Bind to child element of content template by name from outside - c#

How can I get the binding from the outer ConnectingLine (a custom control that binds to FrameworkElements and connects them with a line) to the inner TextBlocks named "Top" and "Bottom" to work? Note that I want the whole FrameworkElements for position information.
<Grid>
<ConnectingLine From="{Binding ElementName=Button1.Top}" To="{Binding ElementName=Button2.Top}" />
<ConnectingLine From="{Binding ElementName=Button1.Bottom}" To="{Binding ElementName=Button2.Bottom}" />
<ToggleButton x:Name="Button1">
<ToggleButton.Template>
<ControlTemplate>
<Grid>
<Rectangle x:Name="Top" />
<Rectangle x:Name="Bottom" />
</Grid>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
<ToggleButton x:Name="Button2">
<ToggleButton.Template>
<ControlTemplate>
<Grid>
<Rectangle x:Name="Top" />
<Rectangle x:Name="Bottom" />
</Grid>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</Grid>
My goal is to be able to bind from within XAML. Ideally with no extra fluff, but a solution involving a custom binding operator or attached properties might be acceptable.
Edit:
How I'd like to have the output:
Each distinct colored column is one of the templated ToggleButtons, already with one dashed ConnectingLine between Top and Bottom elements. The horizontal filled lines are what I'm interested in. Currently I'm achieving what I want from code-behind.

<ToggleButton x:Name="Button">
<ToggleButton.Template>
<ControlTemplate>
<Grid>
<CheckBox x:Name="FindMe" IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsChecked}"/>
</Grid>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
Let me know if it works.

Related

How do i get a Menu in the Window bar?

I would like to know how to get a menu in the window bar, the same as Visual studio does.
It would be good to be able to have File, Edit, etc on the left and the standard Minimize, Maximize and Close buttons on the right. Is this at all possible?
I have tried setting Window WindowStyle="None" and adding my own icons in the bar but it doesnt seem right but is this the only way?
This is what i have at the moment.
<Window
Title="MainWindow"
Height="{x:Static SystemParameters.PrimaryScreenHeight}"
Width="{x:Static SystemParameters.PrimaryScreenWidth}"
Closing="Window_Closing"
WindowState="Maximized">
You must create your custom window chrome using the WindowChrome class:
The Window element in WPF is actually hosted in a non-WPF (non-client) host. This host includes the title bar (caption) and the standard buttons, an icon and the actual frame. This is known as window chrome.
Usually you can only modify the client area of a Window. But whith the help of the WindowChrome class, WPF allows the client area to extend into the non-client area.
The disadvantage is that you would have to basically replicate the original non-client area in order to preserve the original look and feel. But after all you still get some basic behavior like maximizing the window on double click ou of the box.
The following example is very basic, but should give you an idea how to achieve your task:
I highly recommend to follow the provided link to the WindowChrome class and read the remarks section, which contains very valuable information.
You can use the actual system values provided by the static SystemParameters class to get the current dimension values e.g. SystemParameters.WindowResizeBorderThickness which you should use in your custom chrome style.
Also note that to allow WPF to capture mouse events on you custom chrome's input elements like buttons or menus you must set the attached property WindowChrome.IsHitTestVisibleInChrome on each relevant element to true:
WindowChrome.IsHitTestVisibleInChrome="True"
The very basic style that creates the above visual is as followed:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework">
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome NonClientFrameEdges="Right"
ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Background="Transparent">
<AdornerDecorator>
<Border Background="Transparent" Margin="{x:Static SystemParameters.WindowNonClientFrameThickness}">
<ContentPresenter />
</Border>
</AdornerDecorator>
<ResizeGrip x:Name="WindowResizeGrip"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Visibility="Collapsed"
IsTabStop="false" />
<Grid Height="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}, Path=Top}"
Background="#FF3F3F3F"
VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Custom window chrome -->
<StackPanel Grid.Column="0" Orientation="Horizontal"
Margin="{x:Static SystemParameters.WindowResizeBorderThickness}">
<Image Source="{TemplateBinding Icon}" />
<TextBlock Text="{TemplateBinding Title}"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"
Margin="16,0" />
<Menu shell:WindowChrome.IsHitTestVisibleInChrome="True">
<MenuItem Header="CustomChromeMenu">
<MenuItem Header="Action" />
</MenuItem>
</Menu>
</StackPanel>
<StackPanel Grid.Column="1"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Width="45"
Height="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}, Path=Top}"
ToolTip="Minimize window"
ToolTipService.ShowDuration="5000"
shell:WindowChrome.IsHitTestVisibleInChrome="True">
<TextBlock Foreground="{Binding RelativeSource={RelativeSource AncestorType=Control}, Path=Foreground}"
FontFamily="Segoe MDL2 Assets"
FontSize="11"
Text="" />
</Button>
<Button ToolTip="Maximize window"
Width="45"
Height="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}, Path=Top}"
ToolTipService.ShowDuration="5000"
shell:WindowChrome.IsHitTestVisibleInChrome="True">
<TextBlock Foreground="{Binding RelativeSource={RelativeSource AncestorType=Control}, Path=Foreground}"
FontFamily="Segoe MDL2 Assets"
FontSize="11"
Text="" />
</Button>
<Button ToolTip="Close application"
ToolTipService.ShowDuration="5000"
Width="50"
Height="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}, Path=Top}"
shell:WindowChrome.IsHitTestVisibleInChrome="True">
<TextBlock Foreground="{Binding RelativeSource={RelativeSource AncestorType=Control}, Path=Foreground}"
FontFamily="Segoe MDL2 Assets"
FontSize="11"
Text="" />
</Button>
</StackPanel>
</Grid>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ResizeMode"
Value="CanResizeWithGrip">
<Setter TargetName="WindowResizeGrip"
Property="Visibility"
Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
The title bar contains
An icon
A title
A minimize button
A maximize button
A close button
And that's all. You can't add anything.
In VS, the title bar is masked. The "title bar" you see is in fact the content of the window. That is, as you seemed to suspect, the only way.
That's also why tooltips on the three right buttons are in the OS language for most apps (because the title bar is managed by the system), but are in the app language for Visual Studio.
You have to set WindowStyle to None to mask the real title bar.
Then, inside your window, you should add a DockPanel and dock to the top an image and a menu on the left, and 3 buttons on the right.
The minimize button should change the WindowState to Minimized.
The maximize button should change the WindowState to either Normal or Maximized and its icon should be based on the WindowState.
The close button should call the Close() method and/or the Application.Current.Shutdown(0) method.
You should also subscribe to events like MouseLeftButtonDown, MouseLeftButtonUp and MouseMove to move the window.

How to add a border around the entire DataGrid column header?

I've customized the DataGrid a lot, but surprisingly I'm unable to add a border around the entire header area without it breaking.
So this is what I'm trying to accomplish:
The following control is responsible for displaying the header area: DataGridColumnHeadersPresenter.
I added a border around it, and as you can see from the screenshot above, it does work, but the problems begin only when the grid is empty! (this means that also the empty row needs to be removed, which can be done by setting CanUserAddRows="False").
So far, here's my style:
<Border BorderBrush="Red" BorderThickness="1" Grid.Column="1">
<DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" Margin="0,0,0,5"
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}">
</DataGridColumnHeadersPresenter>
</Border>
All I did was wrap it with a border, but now when I apply filtering so that the grid is empty, the header disappears to the right and the application slows down to a crawl.
Demonstration:
If I remove the border, everything works as expected. It seems like the DataGrid is expecting a very specific tree structure, otherwise it just explodes.
I tried changing the template of the DataGridColumnHeadersPresenter, but also that expects a very specific structure which looks like this:
<DataGridColumnHeadersPresenter.Template>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid>
<DataGridColumnHeader x:Name="PART_FillerColumnHeader"
IsHitTestVisible="False" />
<ItemsPresenter />
</Grid>
</ControlTemplate>
</DataGridColumnHeadersPresenter.Template>
If I give the DataGridColumnHeader a BorderBrush and BorderThickness, it doesn't look right, and if I add my own border control anywhere, the same problem arises.
The DataGridColumnHeadersPresenter actually has BorderBrush and BorderThickness properties, but they have no effect at all.
One workaround I found was to set Grid.Column to 0 so that it goes in place of the row headers column header, then just set HeadersVisibility="Column" on the DataGrid so it doesn't look broken, and the problem disappears. Unfortunately I need the row headers, so this is an unacceptable solution.
Default style for DataGrid can be found here, or just right-click it in Visual Studio and go to Edit Template, then Edit a Copy, which is what I did.
There must be an easy way to accomplish this that I'm probably just not seeing right now...
Ok so I just returned to tackle this problem after #jsanalytics pointed out the existence DataGridHeaderBorder, and by analyzing the default tree structure more deeply, with a little bit of trial and error, I managed to get the job done.
I did not want the DataGridHeaderBorder though, which is part of the Windows themes, but replaced it with a regular border.
My implementation:
<DataGridColumnHeadersPresenter Grid.Column="1" x:Name="PART_ColumnHeadersPresenter"
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}">
<DataGridColumnHeadersPresenter.Template>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid HorizontalAlignment="Left">
<ItemsPresenter />
<DataGridColumnHeader x:Name="PART_FillerColumnHeader" IsHitTestVisible="False">
<DataGridColumnHeader.Template>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Border BorderThickness="2" BorderBrush="Red">
<ContentPresenter RecognizesAccessKey="True"
SnapsToDevicePixels="True" />
</Border>
<!--Uncomment if you need these resizing grippers-->
<!--<Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" />
<Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" />-->
</Grid>
</ControlTemplate>
</DataGridColumnHeader.Template>
</DataGridColumnHeader>
</Grid>
</ControlTemplate>
</DataGridColumnHeadersPresenter.Template>
</DataGridColumnHeadersPresenter>
You'll probably need to tweak it for your own needs, but this fills our requirements :-)

WPF RowValidation column of DataGrid placement

Is it possible to place RowValidation column not at the left side? For example, at the right side or somewhere else?
How it looks:
How I want it to looks:
While I can't confirm this 100%, I don't think that your requirements are possible. There is a DataGrid.RowValidationErrorTemplate property which enables you to define a custom template to display when validation errors occur, but it does not enable you to specify the placement of it. According to the DataGrid.RowValidationErrorTemplate Property page on MSDN:
The following example replaces the default row validation feedback with a more visible indicator. When a user enters an invalid value, a red circle with a white exclamation mark appears in the row header.
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
<Grid Margin="0,-2,0,-2"
ToolTip="{Binding RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type DataGridRow}},
Path=(Validation.Errors)[0].ErrorContent}">
<Ellipse StrokeThickness="0" Fill="Red"
Width="{TemplateBinding FontSize}"
Height="{TemplateBinding FontSize}" />
<TextBlock Text="!" FontSize="{TemplateBinding FontSize}"
FontWeight="Bold" Foreground="White"
HorizontalAlignment="Center" />
</Grid>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>

My listview items are not showing up when setting a template for it

I have two templates, one for a textbox and one for a listview, both are just used to give them rounded corners instead of the default rectangle. My textbox needed the "ScrollViewer x:Name="PART_ContentHost" line in order to show text, but that doens't work for the listview. If I take out the Template for the listview the example listviewitem (stuff) will show up. Otherwise it won't and I can't see any other items I add in the code behind. What am I missing in the xaml to get this to work?
Here is my xaml below:
<!-- Design Templates to set the borders of the controls-->
<UserControl.Resources>
<ControlTemplate x:Key="TextBoxTemplate" TargetType="TextBox">
<Border BorderBrush="Black" BorderThickness="1,1,1,.5" CornerRadius="7">
<ScrollViewer x:Name="PART_ContentHost" ></ScrollViewer>
</Border>
</ControlTemplate>
<ControlTemplate x:Key="ListViewTemplate" TargetType="ListView">
<Border BorderBrush="Black" BorderThickness=".5,1,1,1" CornerRadius="7">
</Border>
</ControlTemplate>
</UserControl.Resources>
<!-- Controls -->
<Grid Height="270" Width="400">
<StackPanel Width="390">
<TextBox Height="35" Name="InputTextbox" Template="{StaticResource TextBoxTemplate}" VerticalContentAlignment="Center" TextChanged="InputTextbox_TextChanged"></TextBox>
<ListView Height="235" Name="ResultsListView" Template="{StaticResource ListViewTemplate}" SelectionChanged="ResultsListView_SelectionChanged">
<ListViewItem Content="stuff"></ListViewItem>
</ListView>
</StackPanel>
</Grid>
Your ControlTemplate doesn't have a presenter associated with it. This is why you're not seeing any items. See this page for a working example of how to create a ListView.ControlTemplate:
MSDN: ListView ControlTemplate Example
and here's an updated xaml for your control template:
<ControlTemplate x:Key="ListViewTemplate" TargetType="ListView">
<Border BorderBrush="Black" BorderThickness=".5,1,1,1" CornerRadius="7">
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>

Trying to WPF Tab header, need to make 3 adjustments

I want to alter the styling of some WPF tab headers. I would like to keep all the original styling of the tab headers, except for these three things -
Increase the height of the headers
Make the heights of each header the same. Normally the selected tab has a bigger height, I need the heights of both selected and unselected tabs to be the same.
Add a picture above the text on each header
Here is a before and after image of what I am looking to do -
Anyone know how to do this?
There you go, you can replace Stack panel with your nice images.
Update 1- in order to remove sizing effect when seelcting a tab you'll need to alter the TabItem style (header template is too light for it). Just get a StyleSnooper (http://blog.wpfwonderland.com/2007/01/02/wpf-tools-stylesnooper/) open it with VS2010 recompile it for .NET4, launch, navigate to TabItem and search for:
<Setter Property="FrameworkElement.Margin">
<Setter.Value>
<Thickness>
2,2,2,2</Thickness>
</Setter.Value>
</Setter>
<Setter Property="FrameworkElement.Margin" TargetName="Content">
<Setter.Value>
<Thickness>
2,2,2,2</Thickness>
</Setter.Value>
margins are the values you want to change to fix your 2. Then just put the modified version into the resources, so the app can pick it up. The style contains a lot of handy stuff you can tweak.
<Window x:Class="Immutables.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TabControl TabStripPlacement="Left" x:Name="AreasTabControl" Margin="1">
<TabItem x:Name="AttributesTab">
<TabItem.HeaderTemplate>
<DataTemplate>
<Grid Width="100" Height="40">
<Border BorderThickness="1" BorderBrush="Gray" HorizontalAlignment="Left" VerticalAlignment="Top">
<StackPanel Orientation="Horizontal">
<Rectangle VerticalAlignment="Top"
Width="5" Height="5" Fill="White" />
<Rectangle VerticalAlignment="Top"
Width="5" Height="5" Fill="Blue" />
<Rectangle VerticalAlignment="Top"
Width="5" Height="5" Fill="Red" />
</StackPanel>
</Border>
<TextBlock Margin="0,20,0,0">Go Russia!</TextBlock>
</Grid>
</DataTemplate>
</TabItem.HeaderTemplate>
</TabItem>
</TabControl>
</Grid>
</Window>

Categories