I'm writing a ControlTemplate for toolbars that utilizes a UniformGrid in order to provide multiple columns for toolbar items. So far, it seems that even with mostly default ControlTemplate markup (a la MSDN Documentation), Overflow into the ToolBarOverflowPanel no longer works. I have tried using both the default ToolBarOverflowPanel and a StackPanel, to no effect; the relevant template snippit can be found here.
How do I get Overflow to work properly?
Additionally, when placing a ToolBar control inside a ToolBarTray, how can I detect and set the "Thumb", "Content", and "Overflow Toggle Button" to their correct positions? The documentation for ToolBar ControlTemplates does not seem to cover this.
edit: Also something I'm curious about, how can I tie into the UniformGrid's Column property in my Main window xaml?
edit 2: Got the basic for dealing with ToolBarTray Orientation resolved, just needed to add ControlTemplate triggers for the related attached properties (Trigger Property="ToolBarTray.Orientation" Value="Vertical"). Also, who downvotes a decently written question, seriously?
<ToggleButton DockPanel.Dock="Right"
ClickMode="Press"
IsChecked="{Binding
IsOverflowOpen,
Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}
}"
IsEnabled="{TemplateBinding HasOverflowItems}"
>
<ToggleButton.Template>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="Border"
Background="{StaticResource
{x:Static SystemColors.MenuBarBrushKey}
}"
CornerRadius="0,3,3,0"
SnapsToDevicePixels="true"
>
<Grid>
<Path x:Name="Arrow"
Data="M -0.5 3 L 5.5 3 L 2.5 6 Z"
Fill="{StaticResource {x:Static
SystemColors.ControlTextBrushKey
}}"
Margin="2,3"
VerticalAlignment="Bottom"
/>
<ContentPresenter />
</Grid>
</Border>
</ControlTemplate>
</ToggleButton.Template>
<Popup x:Name="OverflowPopup"
AllowsTransparency="true"
Focusable="false"
IsOpen="{Binding
IsOverflowOpen,
RelativeSource={RelativeSource TemplatedParent}
}"
Placement="Bottom"
PopupAnimation="Slide"
StaysOpen="false"
>
<Border x:Name="DropDownBorder"
Background="{StaticResource
{x:Static SystemColors.MenuBarBrushKey}
}"
BorderBrush="{StaticResource
{x:Static SystemColors.ControlDarkBrushKey}
}"
BorderThickness="1"
>
<StackPanel x:Name="PART_ToolBarOverflowPanel"
Focusable="true"
FocusVisualStyle="{x:Null}"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"
Margin="2"
/>
<!--<ToolBarOverflowPanel
x:Name="PART_ToolBarOverflowPanel"
Focusable="true"
FocusVisualStyle="{x:Null}"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"
Margin="2"
WrapWidth="200"
/>-->
</Border>
</Popup>
</ToggleButton>
Related
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.
I want to include an image directly into a ComboBox template.
I found this part of the code and I believe it is here where I would put it:
<Button x:Name="FlyoutButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" FontWeight="Normal" FlowDirection="{TemplateBinding FlowDirection}" FontSize="{ThemeResource ContentControlFontSize}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Left" MinHeight="{ThemeResource ComboBoxItemMinHeightThemeSize}" Padding="6.5,0,0,0" Grid.Row="1">
<ContentPresenter x:Name="ContentPresenter" Margin="0,0.8,0,0" MinHeight="32.5">
<TextBlock x:Name="PlaceholderTextBlock" Margin="0" Style="{StaticResource ComboBoxPlaceholderTextBlockStyle}" Text="{TemplateBinding PlaceholderText}"/>
</ContentPresenter>
</Button>
I can't put an Image inside contentPresenter because it says I can only set 'Content' once.
If I make something like:
<ContentPresenter x:Name="ContentPresenter" Margin="0,0.8,0,0" MinHeight="32.5">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="PlaceholderTextBlock" Margin="0" Style="{StaticResource ComboBoxPlaceholderTextBlockStyle}" Text="{TemplateBinding PlaceholderText}"/>
<Image Source="ms-appx:///Assets/Arrow.png" />
</StackPanel>
</ContentPresenter>
It actually works but I get an error in my XAML view page: "No installed components were detected. Cannot resolve TargetName PlaceholderTextlock.". And also the image disappears after I select an item.
I'd appreciate some guidance.
I believe you want to set the items inside the combo box to have the image with the textblock. In that case you need to set the ItemTemplate of the combobox like this
<ComboBox Name="hik" ItemTemplate="{StaticResource cmbx}">
And in your page resources you can define the item template for combo box items like this
<DataTemplate x:Key="cmbx">
<StackPanel Orientation="Horizontal" Background="Aqua">
<TextBlock HorizontalAlignment="Left" Margin="0,0,0,0" Foreground="Black" TextWrapping="Wrap" Text="Some Text" VerticalAlignment="Top"/>
<Image Source="/Assets/1.png" Stretch="Uniform" Height="100" Width="100" />
</StackPanel>
</DataTemplate>
Once that is done when you run it on clicking the combobox you can see the list with image
Hope this helps!
I try to create a custom ComboBox called MyComboBox. It has a button for switching between the previous and next items.
I store the Background color of the base in BaseBackground. This is useful, as I don't want the FrontGlyph to have the Background inherited from the templated parent.
This is my WPF code:
<Style x:Key="{x:Type local:MyComboBox}" TargetType="{x:Type local:MyComboBox}">
</Style.Resources>
<Setter Property="Focusable" Value="False" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.CanContentScroll" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyComboBox}">
<Grid Cursor="Hand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <!-- A glyph will come here -->
<ColumnDefinition Width="*" /> <!-- The base combo box can take as much space as it can. -->
</Grid.ColumnDefinitions>
<Path x:Name="FrontGlyph" Grid.Column="0" Data="M 0.0 16.0 L 6.0 0.0 L 6.0 16.0 Z" Fill="{TemplateBinding BaseBackground}" Stretch="Fill" />
<Grid x:Name="BaseComboBox" Grid.Column="1" Background="{TemplateBinding BaseBackground}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <!-- Previous item -->
<ColumnDefinition Width="Auto" /> <!-- Next item -->
<ColumnDefinition Width="*" /> <!-- Content Presenter -->
<ColumnDefinition Width="Auto" /> <!-- Drop down button -->
</Grid.ColumnDefinitions>
<Button x:Name="Prev" Grid.Column="0" Background="{TemplateBinding Background}" Style="{StaticResource MyUIButton}">
<Path VerticalAlignment="Center" Data="M 4.5 0.5 L 0.5 4.5 L 4.5 8.5 Z" Fill="Black" />
</Button>
<Button x:Name="Next" Grid.Column="1" Background="{TemplateBinding Background}" Style="{StaticResource MyUIButton}">
<Path VerticalAlignment="Center" Data="M 0.5 0.5 L 4.5 4.5 L 0.5 8.5 Z" Fill="Black" />
</Button>
<ContentPresenter x:Name="ContentSite" Grid.Column="2" IsHitTestVisible="False" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" VerticalAlignment="Stretch" HorizontalAlignment="Left" />
<ToggleButton x:Name="ToggleButton" Template="{StaticResource ComboBoxToggleButton}" Grid.Column="3" Focusable="false" ClickMode="Press" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Background="{TemplateBinding Background}" />
<Popup x:Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
<Grid x:Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="DropDownBorder" BorderThickness="1" BorderBrush="{TemplateBinding Background, Converter={StaticResource LightenBrushColor}, ConverterParameter=0.5}" Background="{TemplateBinding Background}" />
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BaseBackground" Value="Goldenrod" /> <!-- This does not work -->
<!--<Setter TargetName="FrontGlyph" Property="Fill" Value="Goldenrod" />
<Setter TargetName="BaseComboBox" Property="Background" Value="{Binding Path=Fill, ElementName=FrontGlyph}" />--> <!-- These 2 lines do work. -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And this is the source code of MyComboBox:
class MyComboBox : ComboBox
{
public static readonly DependencyProperty BaseBackgroundProperty;
public SolidColorBrush BaseBackground { get { return (SolidColorBrush)GetValue(BaseBackgroundProperty); } set { SetValue(BaseBackgroundProperty, value); } }
static MyComboBox()
{
BaseBackgroundProperty = DependencyProperty.Register("BaseBackground", typeof(SolidColorBrush), typeof(MyComboBox), new FrameworkPropertyMetadata(Brushes.Lime, FrameworkPropertyMetadataOptions.AffectsRender, OnBaseBackgroundPropertyChanged));
}
public MyComboBox()
{
DefaultStyleKey = typeof(MyComboBox);
}
private static void OnBaseBackgroundPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
// This is not called when the trigger tries to set BaseBackground when the mouse is over the control
}
}
When I hover the mouse over the control, it should change colors. I should be able to do this via changing the BaseBackground, since both FrontGlyph and BaseComboBox have their background color bound to that. Nevertheless the code compiles but the change does not happen. Not just on the UI, but if I debug the code, I don't see the change in C# either.
If I change the background color of the FrontGlyph and bind the BaseComboBox.BackgroundColor to that, it works nicely.
Could someone explain why the changes of my custom property BaseBackground is not registered? It must not be the standard "Implement the INotifyPropertyChanged interface." issue, as I use Brush as my property and Brushes work pretty well everywhere else. :)
My implementation might look silly. Well, I'm kinda new to WPF. Plus I didn't want to burden you with the whole implementation, just tried to replicate the critical parts.
UPDATE
I've found out that in my source code I set BaseBackground = new SolidColorBrush(...) if some conditions are fulfilled. If I remove this line of code, now the triggers work and BaseBackground gets assigned the Goldenrod color.
But I wonder, why changing a DependencyProperty from C# code prevents it working from XAML markup. Besides, I need both of them to work.
Thank you.
Summary
I think what you probably want to do is register the BaseBackgroundProperty under the name "BaseBackground" (rather than "Background") since your xaml is trying to set a property with that name.
Details
Looking at your code, I think the reason your MyComboBox.BaseBackground property is never set is because it is being registered with the dependency property system using the name "Background". This means you have a property registered in a base class as "Background" (Control.BackgroundProperty), and a property registered in your derived class as "Background" (MyComboBox.BaseBackgroundProperty). So in theory, setting "Background" in xaml should end up setting MyComboBox.BaseBackgroundProperty while setting "Control.Background" should end up setting the Control.Background property.
While that works in theory, I have no idea if it works in practice. It is also not really the way to go about doing things. You could override the metadata for Control.BackgroundProperty in your type initializer (aka static constructor) if you wanted to modify the existing property for your class in some way, but I don't think that is what your intention is here. Your xaml is trying to set the non-existent property named "BaseBackground".
Based on your question update, it looks like what you are doing is setting a local value on the dependency property, which might be breaking the template binding. This can happen with one way bindings in certain situations where the target gets set by some other means. It seems like you are setting the source in this case, but it is possible that it is causing the binding to break.
You can debug the binding by setting the PresentationTraceSources.TraceLevel property on it to "High". (You might need to use the standard {Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay} syntax rather than the {TemplateBinding} shortcut syntax in order to attach the property – I am not sure.) Then, run the application and look at the debug output. It will tell you if the binding gets broken.
If the binding is in fact being broken, there are different things you can do to fix it, depending on your use cases.
You can set the binding mode to TwoWay to keep it from breaking.
You can try only setting it from code using SetCurrentValue instead of SetValue, as many controls tend to do when modifying their own dependency properties from code.
Potentially related information:
DependencyProperty Value Precedence
In WPF Why Doesn't TemplateBinding Work Where Binding Does?
Debugging Data Bindings
Control Authoring Overview
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.
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>