I have a project that adds tabs to the view using a TabControl with DataTemplates like so:
<TabControl Name="dcTabControl"
ItemsSource="{Binding Tabs}"
SelectedItem="{Binding SelectedTabViewModel}"
Height="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=DataContext.MainContentHeight}">
<TabControl.Resources>
<!-- Removed numerous other tabs to save space -->
<!-- System Setup tab -->
<DataTemplate DataType="{x:Type vm:SystemSetupViewModel}">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<v:SystemSetupUserControl />
</ScrollViewer>
</DataTemplate>
<!-- About tab -->
<DataTemplate DataType="{x:Type vm:AboutViewModel}">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<v:AboutUserControl />
</ScrollViewer>
</DataTemplate>
</TabControl.Resources>
</TabControl>
Each ViewModel has a Header property that is used to populate the text of the tab (e.g. "About").
I now have a requirement to change the "About" text to an icon. I have tried this but it doesn't change anything.
<!-- About tab -->
<DataTemplate DataType="{x:Type vm:AboutViewModel}">
<TabItem>
<TabItem.Header>
<Image Name="AboutTabImage" Height="auto" Width="auto" Source="Images/About.png" />
</TabItem.Header>
<TabItem.Content>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<v:AboutUserControl />
</ScrollViewer>
</TabItem.Content>
</TabItem>
</DataTemplate>
How can I get an icon in place of the text?
UPDATE Adding code to show how Header property is bound to Tab.
The Header is bound to the Tab using this Style
<!-- Standard Tab Style -->
<Style x:Key="TabStyle" TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Width" Value="Auto" />
<Setter Property="Padding" Value="10,5,10,5" />
</Style>
I am now thinking I have to create a new style to use an icon instead of text, but not sure how I would apply that style to the data template.
You may add a DataTrigger to the TabItem Style that changes the Header to an Image if the Header property contains the string "About":
<Style TargetType="TabItem">
...
<Setter Property="Header" Value="{Binding Header}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Header}" Value="About">
<Setter Property="Header">
<Setter.Value>
<Image Source="Images/About.png"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
Related
I'm learning WPF and developing a dynamic Menu which is driven by data binding of it's ItemsSource to an ObservableCollection. To do this I have a simple MenuItemViewModel and a HierarchicalDataTemplate for automatic binding of MenuItems to it.
The problem I have is that Command property doesn't work for top level menu items. Despite it is set, a MenuItem doesn't react on mouse click and doesn't get disabled if Command cannot be executed. Simply it's like it's not getting bound.
For lower level menu items though, it works as intended. I think this should be a problem of my HierarchicalDataTemplate but I can't find it, because as I see there's no code in template which might affect command binding of only top-level MenuItems.
MenuItemViewModel implements INotifyPropertyChanged and contains following public properties:
string Text
Uri ImageSource
ICommand Command
ObservableCollection<MenuItemViewModel> Children
HierarchicalDataTemplate for MenuItem in my Window.Resources is as follows:
<HierarchicalDataTemplate DataType="{x:Type common:MenuItemViewModel}"
ItemsSource="{Binding Path=Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding Command}" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" />
<TextBlock Text="{Binding Text}" VerticalAlignment="Center"/>
</StackPanel>
</HierarchicalDataTemplate>
Can you please point me on my mistake?
EDIT: Top-level MenuItem doesn't contain any children (i.e. Children collection of associated ViewModel is empty).
Thanks to #sTrenat comment I've come to solution below.
<Menu.Resources>
<!-- cancel sharing of image so Icon will work properly -->
<Image x:Key="MenuIcon" Source="{Binding ImageSource}" x:Shared="False"/>
</Menu.Resources>
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Header" Value="{Binding Text}" />
<Setter Property="Icon" Value="{StaticResource MenuIcon}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="ItemsSource" Value="{Binding Children}" />
<!-- centering MenuItem's Header -->
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
<!-- setting Icon to null when ImageSource isn't specified -->
<Style.Triggers>
<DataTrigger Binding="{Binding ImageSource}"
Value="{x:Null}">
<Setter Property="Icon" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.ItemContainerStyle>
I have three canvases and each one contains textboxes.
The first canvas contains a textbox bigger than the canvas and should be on top of the below canvases.
I tried to use z-index but it seems canvases are always on top on the textboxes.
You could explictly create the item containers, i.e. ListBoxItems.
The ListBoxItems will be direct child elements of the ListBox's ItemsPanel and hence have a common parent element, which is required to make the ZIndex property work:
<ListBox>
<ListBoxItem Panel.ZIndex="1">
<Canvas ...>
...
</Canvas>
</ListBoxItem>
<ListBoxItem>
<Canvas ...>
...
</Canvas>
</ListBoxItem>
<ListBoxItem>
<Canvas ...>
...
</Canvas>
</ListBoxItem>
</ListBox>
I would also apply some styling to make your code way shorter and easier to maintain. Should look kinda like that before applying Clemenses solution:
<ListBox>
<ListBox.Resources>
<Style TargetType="{x:Type Canvas}">
<Setter Property="Width" Value="500" />
<Setter Property="Background" Value="Blue" />
<Setter Property="Height" Value="40" />
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Width" Value="30" />
<Setter Property="Canvas.Top" Value="10" />
<Setter Property="Height" Value="20" />
</Style>
</ListBox.Resources>
<Canvas Name="firstCanvas"
Top="80"
Left="100"
ZIndex="3">
<TextBox Name="BigText"
Background="White"
Canvas.ZIndex="6"
Canvas.Left="50"/>
<TextBox Canvas.Left="100"/>
<TextBox Canvas.Left="150"/>
<TextBox Canvas.Left="200"/>
</Canvas>
<Canvas Top="80"
Left="100"
ZIndex="1">
<TextBox Canvas.Left="100"/>
<TextBox Canvas.Left="150"/>
<TextBox Canvas.Left="200"/>
</Canvas>
<Canvas Top="80"
Left="100"
ZIndex="2">
<TextBox Canvas.Left="100"/>
<TextBox Canvas.Left="150"/>
<TextBox Canvas.Left="200"/>
</Canvas>
</ListBox>
Similar to gmail's Select button, I wanted to create a ComboBox for the ListView which allows the user to quickly select entries of their choosing (ex. All, None, Read, Unread). However, the selected value would display a tri-state CheckBox equivalent to All, Some, or None of the entries being selected. I succeeded in doing so. Below is the xaml for an example Window utilizing this feature(*):
<Window x:Class="WPFTest.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfTest="clr-namespace:WPFTest.ViewModels"
xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Mvvm;assembly=Microsoft.Practices.Prism.Mvvm.Desktop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFTest.Models"
mc:Ignorable="d" Title="WPF Test" Height="221.256" Width="605"
d:DataContext="{d:DesignInstance wpfTest:MainWindowViewModel, IsDesignTimeCreatable=True}"
prism:ViewModelLocator.AutoWireViewModel="True" WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="/WPFTest;Component/Resources/Resources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ListView
ItemsSource="{Binding Entries}"
SelectedValue="{Binding SelectedEntry}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ScrollViewer.CanContentScroll="True"
SelectionMode="Single"
Margin="10">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox
IsChecked="{Binding Selected}"
Command="{Binding DataContext.RowSelectedCommand, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumnHeader>
<ComboBox
ItemsSource="{Binding Options.Items}"
SelectedValue="{Binding Options.SelectedItem}"
ItemTemplateSelector="{DynamicResource itemTemplateSelector}"
HorizontalAlignment="Stretch"
Margin="0,0,0,0"
VerticalAlignment="Stretch"
Width="44"
Height="34"
FontSize="20"
VerticalContentAlignment="Top"
HorizontalContentAlignment="Left">
<ComboBox.Resources>
<DataTemplate x:Key="selectedTemplate">
<TextBlock
x:Name="displayText"
Text="{Binding DataContext.Options.SelectedDisplay, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
FontSize="20"
Height="22"/>
</DataTemplate>
<DataTemplate x:Key="dropDownTemplate">
<TextBlock Text="{Binding}" FontSize="12"/>
</DataTemplate>
<local:ComboBoxItemTemplateSelector
x:Key="itemTemplateSelector"
SelectedTemplate="{StaticResource selectedTemplate}"
DropDownTemplate="{StaticResource dropDownTemplate}"/>
</ComboBox.Resources>
</ComboBox>
</GridViewColumnHeader>
</GridViewColumn>
<GridViewColumn
Width="Auto"
DisplayMemberBinding="{Binding Type, Mode=OneWay}"
Header="Type"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Recently I was asked to make a style for every ComboBox on our screens - changing their backgrounds. Because I'm on Windows8, setting the Background alone isn't enough. Using this tutorial I was able to create the ControlTemplate to get the correct behavior, with one minor error fix:
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="True"/>
<!-- Comment out the following, it throws an error. -->
<!--<Condition>
<Condition.Value>
<sys:Boolean>False</sys:Boolean>
</Condition.Value>
</Condition>-->
</MultiTrigger.Conditions>
And usage:
<Style
TargetType="{x:Type ComboBox}">
...
<Setter
Property="Template"
Value="{StaticResource ComboBoxStyle1}" />
</Style>
This successfully styles the ComboBox Background. However, revisiting the former screen, I noticed that this breaks my gmail-like display.
How can I get this ControlTemplate and the dynamic DataTemplate to cooperate?
(*) ViewModels and Models can be provided if necessary for a solution. Or see full working example.
I'm not sure why you doing so complicated ComboBox - you defined ItemTemplateSelector twice...
Ok - here is my 2 cents: ComboBox is lookless control. It based on ControlTemplate target type = ComboBox. Inside ComboBox ControlTemplate you will find ContentPresenter. Whatever coming into Content of ContentPresenter could be styled with DataTemplate. Generally - when you define DataTemplate it wrap only ContentPresenter or ItemsPresenter for range based controls - not the whole ControlTemplate obviously.
So if you want to change 'Selected' template for ComboBox is ok but all other data should be defined via DataTemplate for this type {x:Type local:SomeType} that will be used by ComboBox.
Also - consider using #galakt suggestion: use Style with TargetType - it easy to read, refactor, find, understand...
Color the Background
As per the OP, following this tutorial, you generate the ControlTemplate.
To do this, you can right-click on the ComboBox element in design mode
in Visual Studio 2012 or 2013 and select the “Edit template” option
and then the “Edit a copy…” option.
Again note the bug fix from the OP. Changing the background can then be done by:
<Border
x:Name="templateRoot"
BorderBrush="#FFACACAC"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True"
Background="{StaticResource MyComboBackgroundBrush}"> <!-- option 1 -->
<!-- <Border.Background> -->
<!-- option 2 -->
<!-- <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF0F0F0" Offset="0"/>
<GradientStop Color="#FFE5E5E5" Offset="1"/>
</LinearGradientBrush>-->
<!-- option 3 -->
<!-- <SolidColorBrush Color="Yellow"/> -->
<!-- </Border.Background>
<Border x:Name="splitBorder" BorderBrush="Transparent" BorderThickness="1" HorizontalAlignment="Right" Margin="0" SnapsToDevicePixels="True" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
<Path x:Name="Arrow" Data="F1M0,0L2.667,2.66665 5.3334,0 5.3334,-1.78168 2.6667,0.88501 0,-1.78168 0,0z" Fill="#FF606060" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center"/>
</Border> -->
</Border>
Interact with the DataTemplate
In the resource file, following the above instructions, you declared:
<ControlTemplate x:Key="ComboBoxControlTemplateBasic" TargetType="{x:Type ComboBox}">
...
<ContentPresenter
x:Name="contentPresenter"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
... />
...
</ControlTemplate>
Create a copy of it with the following change, where selectedTemplate is the key of the DataTemplate you want to cooperate with the ControlTemplate:
<ControlTemplate x:Key="ComboBoxControlTemplateHeader" TargetType="{x:Type ComboBox}">
...
<ContentPresenter
x:Name="contentPresenter"
ContentTemplate="{DynamicResource selectedTemplate}"
... />
...
</ControlTemplate>
Declare the appropriate styles:
<Style
TargetType="{x:Type ComboBox}">
<Setter Property="Template"
Value="{StaticResource ComboBoxControlTemplateBasic}" />
<!-- other generic style setters -->
</Style>
<Style
x:Key="ComboBoxHeader"
x:Name="ComboBoxHeader"
TargetType="{x:Type ComboBox}"
BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Template"
Value="{StaticResource ComboBoxControlTemplateHeader}" />
<Setter Property="ItemTemplateSelector"
Value="{DynamicResource itemTemplateSelector}"/>
<!-- other specific style setters -->
</Style>
Results
Every ComboBox shares the same look.
The dynamic use of DataTemplate selectedTemplate allows each ListView header gmail-like ComboBox to have the same look with unique binding values.
Take for example the OP ComboBox header plus the following ComboBox with the same item source:
<ComboBox
HorizontalAlignment="Left"
Margin="10,152,0,0"
VerticalAlignment="Top"
Width="120"
ItemsSource="{Binding Options.Items}"
SelectedValue="{Binding Options.SelectedItem}"/>
<GridViewColumnHeader>
<ComboBox
ItemsSource="{Binding Options.Items}"
SelectedValue="{Binding Options.SelectedItem}"
Style="{StaticResource ComboBoxHeader}">
<ComboBox.Resources>
<DataTemplate x:Key="selectedTemplate">
<TextBlock
x:Name="displayText"
Text="{Binding DataContext.Options.SelectedDisplay, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
FontSize="20"
Height="22"/>
</DataTemplate>
<DataTemplate x:Key="dropDownTemplate">
<TextBlock Text="{Binding}" FontSize="12"/>
</DataTemplate>
<local:ComboBoxItemTemplateSelector
x:Key="itemTemplateSelector"
SelectedTemplate="{StaticResource selectedTemplate}"
DropDownTemplate="{StaticResource dropDownTemplate}"/>
</ComboBox.Resources>
</ComboBox>
</GridViewColumnHeader>
I have been trying to have a popup whenever I press a button.
However. Lets say that I have a lot of text on that popup and then it can go over the application window bounds.
How I can I tell the popup to keep within the application window bounds in xaml code?
It would be great with some examples in xaml code on how to tell the Popup element to keep within the application
Thanks
Xaml code:
...
<ToggleButton Grid.Row="1" Grid.Column="1" Margin="0,4"
x:Name="Somethingname"
Height="30"
Template="{StaticResource ComboBoxToggleButton}"
Content="Hello man"/>
<Popup Grid.Row="1" Grid.Column="1" x:Name="PopupMe"
Style="{StaticResource PopupStyle}"
ClipToBounds="True">
<TreeView ItemsSource="{Binding SomeObservableCollection}"
Width="{Binding ElementName=DetaljerHjemmmelToggler, Path=ActualWidth}"
ItemTemplate="{StaticResource SomeTemplate}"
cal:Message.Attach="[Event SelectedItemChanged] = [Action TreeView_SelectedItem($this, $source)]">
</TreeView>
</Popup>
...
In ResourceDictionary XAML file:
<HierarchicalDataTemplate x:Key="SomeTemplate" ItemsSource="{Binding ChildrenCollection}">
<TextBlock Text="{Binding Path=TekstBlaaah}" Style="{StaticResource Label}"/>
</HierarchicalDataTemplate>
<Style TargetType="{x:Type Popup}" x:Key="PopupStyle">
<Setter Property="IsOpen" Value="{Binding ElementName=Somethingname, Path=IsChecked}"/>
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="PlacementTarget" Value="{Binding ElementName=Somethingname}"/>
<Setter Property="PopupAnimation" Value="Slide"/>
<Setter Property="StaysOpen" Value="False" />
<Setter Property="Placement" Value="Bottom"/>
</Style>
Check it out:
Custom popup and windows in WPF the MVVM way
Sample is also available
Can you please tell me how to change content template when, in WPF, when it is clicked on different Menu Item headers. I've defined user control that I can put it as a template.
For example: Menu Items are: Home, Players, Team . when i click on Home I want that specific template in my Content Control to pop up, tehen when I click on Players I want another template (list of players) to pop in Content Control as template.
How to do that with triggers in XAML?
Thank you very much :)
You can use a ContentControl to host whatever your content will be, and set the ContentControl.ContentTemplate based on how you want to draw your content.
As a very basic example,
<ContentControl x:Name="MyContentControl">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding }" Value="Home">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<local:MyHomeUsercontrol />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding }" Value="Players">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<local:MyPlayersUsercontrol />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding }" Value="Team">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<local:MyTeamUsercontrol />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style>
</ContentControl.Style>
</ContentControl>
And on MenuItem.Click
MyContentControl.Content = "Home"; // or "Players" or "Team"
In this example I'm using a string for the ContentControl.Content, however if you were to use a class object such as a HomeViewModel or PlayersViewModel, your XAML could be simplified to use implicit data templates, which are templates that automatically get used whenever WPF tries to draw a specific class
<Window.Resources>
<DataTemplate DataType="{x:Type HomeViewModel}">
<local:MyHomeUserControl />
</DataTemplate>
<DataTemplate DataType="{x:Type PlayersViewModel}">
<local:MyPlayersUserControl />
</DataTemplate>
<DataTemplate DataType="{x:Type TeamViewmModel}">
<local:MyTeamUserControl />
</DataTemplate>
</Window.Resources>
<ContentControl x:Name="MyContentControl" />
and
MyContentControl.Content = new HomeViewModel();
How about using a tab control with tab placement at the top? it would be much cleaner that way..
Edit; Example:
<TabControl>
<TabItem>
<TabItem.Header>
<TextBlock Text="Football header!"/>
</TabItem.Header>
<TabItem.Content>
<Button Content="Push for football!"/>
</TabItem.Content>
</TabItem>
</TabControl>
this might help also; http://www.switchonthecode.com/tutorials/the-wpf-tab-control-inside-and-out