Bind page title to a text block in a style - wpf

OK, I have defined a style for Navigation Window. I have successfully styled Navigation buttons and even added page breadcrumbs to the Navigation menu. What I want is to add Page title next to the breadcrumbs:
Style x:Key="{x:Type NavigationWindow}" TargetType="NavigationWindow">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="NavigationWindow">
<DockPanel Background="{StaticResource WindowBackgroundBrush}">
...
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
....
<StackPanel Grid.Column="4" Orientation="Horizontal">
<TextBlock Foreground="Gray"
VerticalAlignment="Center"
Text="{Binding Path=Title,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Page}}}" />
</StackPanel>
</Grid>
</DockPanel>
...
</ControlTemplate>
</Setter.Value>
</Setter>
The binding doesn't work for the last TextBlock. (However it works just fine if not used within a style, but in regular XAML page code-behind) I have no idea why. Help? How to make it display the current page title? Thanks.

The problem is that within the ControlTemplate, there is no ancestor of type Page. The control you apply the template to may have an ancestor of type Page, but the ControlTemplate itself doesn't know about that. It only knows about ancestors in its own logical tree.
To help mitigate this issue, the WPF designers added the TemplateBinding markup extension, which allows you to apply the value of a property on the templated control to a property in the control template.
So, on the NavigationWindow, you should create a property that exposes the Title of the Page. Then you can use the following mark-up to bind to it:
Text="{TemplateBinding TitleProperty}"

Related

Showing or hiding a control in WPF template based on bindings

I am new to WPF binding/templating. I have some basic questions about a templated TabControl I have as below :
<TabControl x:Name="tcTabs" ItemsSource="{Binding Rooms, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Margin="5" BorderThickness="1" IsSynchronizedWithCurrentItem="True">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="130"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="22"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Grid.Column="0" BorderThickness="0" ItemsSource="{Binding Messages}" DisplayMemberPath="Raw" />
<ListBox Grid.Row="0" Grid.Column="1" BorderThickness="1,0,0,0" BorderBrush="#FFBBBBBB" ItemsSource="{Binding Users}" DisplayMemberPath="Nick" />
<TextBox Grid.Row="1" Grid.ColumnSpan="2" BorderThickness="0,1,0,0" BorderBrush="#FFBBBBBB" Height="22" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
The TabControl contains in each tab 2 list boxes and a textbox. One of the listboxes contains user names is is not necessary all the time.
There are 3 kinds of tabs, Server tabs, room tabs and private tabs. In private and server tabs the user list should not exist or be hidden.
I have an enum on the bound room object :
public enum IRCRoomType
{
Server,
Channel,
Private
}
How do I automatically hide the user list based on the enum, I have seen samples of 2 approaches, the binding on visibility with a converter or a trigger. Which is the better approach and are there any more?
When there are no tabs, and the first tab is created it is not automatically selected, how do I select it?
Is there a way of impacting the item styles inside the listboxes depending on tab type? How would I acheive this?
I am just looking for links/hints and not for actual solutions, but if you can give code then that would be a bonus!
It depends on how complicated code. If it's simple I rather use Trigger (you have everything which belows to UI in XAML), but if code is much more complicated consider using Converters (It's actually simpler to use it)
Bind to SelectedIndex of List and set it to 0?
Yes, of course, you can use ContentControl with DataTemplate (Or just DataTemplate in some cases) Some code where I use it:
<ListBox>
<ListBox.Resources>
<DataTemplate DataType="{x:Type your_namespace:your_type}">
... your code ...
</DataTemplate>
<DataTemplate DataType="{x:Type system:String}">
... your code ...
</DataTemplate>
</ListBox.Resources>
</ListBox>
Code you posted is actually a new Template, but you've changed the Style. Please consider override some Template.
Best regards

Partially templated ListBox.ItemTemplate

I'm creating a custom control and I'm trying to create partially specified template for list box items. The template has some predefined parts and there should be another part that can be templated when using the control.
For this I have created a dependency property named SuggestionItemTemplate like so:
public static readonly DependencyProperty SuggestionItemTemplateProperty =
DependencyProperty.Register("SuggestionItemTemplate",
typeof(DataTemplate),
typeof(AutoSuggestTextBox),
new PropertyMetadata(null));
In my custom controls' generic.xaml I have:
<Style TargetType="local:AutoSuggestTextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:AutoSuggestTextBox">
<Grid>
<ListBox x:Name="ItemsControl">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0"
ContentTemplate="{TemplateBinding SuggestionItemTemplate}"
Content="{Binding}" />
<ToggleButton Grid.Column="1"
x:Name="DetailsHover"
ClickMode="Hover"
Style="{StaticResource DetailsToggleButtonStyle}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Unfortunatelly, this does not work as it's not possible to use TemplateBinding from inside ContentPresenter nested into DataTemplate. (The member "SuggestionItemTemplate" is not recognized or is not accessible.)
I also tried to use ancestor binding (available in Silverlight 5) like:
<ContentPresenter Grid.Column="0"
ContentTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:AutoSuggestTextBox}, Path=SuggestionItemTemplate}"
Content="{Binding}" />
But this results in binding error:
Error: System.Exception: BindingExpression_CannotFindAncestor
I suppose this happens because I'm inside ControlTemplate of my custom control and "local:AutoSuggestTextBox" is not defined anywhere in the style.
The third option that I tried was to apply ContentTemplate in OnApplyTemplate override but this also doesn't work:
var cp = itemsControlElement.ItemTemplate.LoadContent() as ContentPresenter;
cp.ContentTemplate = SuggestionItemTemplate;
In all cases, I get my grid with two columns, toggle button is visible but content presenter simple prints out view model's type name. (I believe this is the default behavior if the ContentTemplate is null).
Is this even possible to do? Are there any other ways to specify a partial template and then only add customized template part when necessary?
As a workaround for now, I can specify
ItemTemplate="{TemplateBinding SuggestionItemTemplate}"
for the list box and then copy/paste the generic template everywhere I use this control. But this is the behavior I'm hoping to avoid in the first place.
Thanks!
edit: I used the code tags for all blocks of code, but they're not highlighted for some reason. :/
It is possible to walk through Visual Ancestors in the OnApplyTemplate method, find your ContentPresenter(s) and set the ItemTemplate on that. To my mind, this is fine for a single item, but not so much in an ItemsControl scenario.
You could achieve what you are after using your own custom Control. Just give it a Content dependency property of type Object, and a Template DP of type DataTemplate (and multiples of the two if you fancy), and you can set up the root visual style and templates in the default style for your Control.
In this specific case, I would suggest that the best approach is to put your ToggleButton in the ListBoxItem template instead by customising the ListBox.ItemContainerStyle. It is easy to modify the default Control Template using Expression Blend, and the DataContext of the ToggleButton will not change, so the changes to your own logic should be minimal.
Edit: If you mean to use a number of different data templates, perhaps Implicit Data Templates will be more suitable.
I managed to solve this using a different approach. I used ancestor binding but instead of trying to reach the root control (my AutoSuggestTextBox) from the DataTemplate, I ask for a reference to my ListBox (here named ItemsControl).
However, since the ListBox doesn't have the SuggestionItemTemplate property, I sub-classed it to my own CustomListBox where I implemented that property. It all comes down to this code snippet:
<Style TargetType="local:AutoSuggestTextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:AutoSuggestTextBox">
<Grid>
<local:CustomizableListBox x:Name="ItemsControl"
SuggestionItemTemplate="{TemplateBinding SuggestionItemTemplate}">
<local:CustomizableListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0"
ContentTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:CustomizableListBox}, Path=SuggestionItemTemplate}"
Content="{Binding}" />
<ToggleButton Grid.Column="1"
x:Name="DetailsHover"
ClickMode="Hover"
Style="{StaticResource DetailsToggleButtonStyle}" />
</Grid>
</DataTemplate>
</local:CustomizableListBox.ItemTemplate>
</local:CustomizableListBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Designing a custom edit control similar to hotmail style "To" box

I am desiging a custom control for use in my application which simulates a hotmail style "To" textbox which lets the user enter semicolon delimited strings. The control behaves like a textbox, on each ener press or entering semicolon a box(a textblock infact) gets created containing text entered which can be manipulated individually.
The control is implemented by means of a listview with its ItemPresenter set to a WrapPanel
The XAML looks like below:
<ListView x:Name="col" ItemContainerStyle="{StaticResource ContainerStyle}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<local:MyWrapPanel Orientation="Horizontal" MinWidth="400"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
<Style TargetType="{x:Type ListViewItem}" x:Key="ContainerStyle">
<Setter Property="ContentTemplate" Value="{StaticResource BoxView}" />
<Style.Triggers>
<Trigger Property="Tag" Value="Edit">
<Setter Property="ContentTemplate" Value="{StaticResource BoxViewEdit}" />
</Trigger>
</Style.Triggers>
</Style>
The control template for the listviewitems (boxes) looks like:
<DataTemplate x:Key="BoxView">
<Border BorderThickness="1" BorderBrush="Brown" Background="Beige" Margin="1,1,1,0" CornerRadius="6" >
<StackPanel Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Margin="5,5,5,0" Height="20" Text="{Binding XPath=''}"/>
<DockPanel Grid.Column="1" Grid.Row="0">
<Image Source="edit8.png" MouseLeftButtonUp="edit_MouseLeftButtonUp" ToolTip="Edit" Margin=" 10,0,0,0"></Image>
<Image Source="cancel8.png" MouseLeftButtonUp ="cancel_MouseLeftButtonUp" ToolTip="Remove" Margin=" 5,0,5,0"></Image>
</DockPanel>
</Grid>
</StackPanel>
</Border>
</DataTemplate>
Actually the listview is data bound to a set of xmlnodes having common parent.
The look and feel of the control has to be given such that it looks like a text box.
Now, I have the listview items bound to the xml nodes of an xml document, to show the textbox I add an empty xmlnode in the document and change the control template of the coressponding listviewitem so that it shows as a textbox.
On pressing enter in the text box a new xml node gets appended to the underlying xml at second last position containing innertext set to the textbox text(last positon is dummy node)
The template used for textbox is:
<DataTemplate x:Key="BoxViewEdit">
<TextBox Margin="0,5,5,0" Background="White" MaxWidth="400" BorderThickness="1" Text="{Binding XPath=''}"/>
</DataTemplate>
But the idea of adding an empty dummy xmlnode in the underlying source xml so that i could simulate editing of the listview seems hacky. Is there any cleaner way of doing this.
What i want is to wrap the text box as shown in figure with the contents of the wrappanel seamlessly without adding it to the wrappanel itself by means of creating a dummy xmlnode.
I had the same issue and found some solutions; you can check out the responses and comments here.

Having Trouble Styling and Editing AvalonDock Document Tab Panel

I'm trying to figure out how to make the VS2010 style for AvalonDock to function a little more like VS2010. The problem that I'm running into is that when there are more tabs than can fit in the header area there is no indication to the user that there are more tabs.
I thought that the tab headers were just clipped and not visible. I have a custom copy of the VS2010 style and went to the DocumentPane style:
<!--DocumentPane-->
<Style x:Key="{x:Type ad:DocumentPane}" TargetType="{x:Type ad:DocumentPane}"> ...
And found the tab headers (I think) as a "ad:DocumentTabPanel". I wrapped this in a ScrollViewer:
<ScrollViewer Style="{StaticResource ResourceKey=TabHeaderScrollViewer}" CanContentScroll="True">
<ad:DocumentTabPanel
x:Name="paneTabsPanel"
Panel.ZIndex ="1"
IsItemsHost="True"
TabItemStyle="{StaticResource CustomDocumentTabItemStyle}"/>
</ScrollViewer>
The scroll viewer is setup to have a custom style on it:
<Style x:Key="TabHeaderScrollViewer" TargetType="ScrollViewer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollViewer">
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<RepeatButton Command="ScrollBar.PageLeftCommand"></RepeatButton>
<ScrollContentPresenter Grid.Column="1"
x:Name="ScrollContentPresenter"
Cursor="{TemplateBinding Cursor}"
Margin="{TemplateBinding Padding}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
<RepeatButton Grid.Column="2" Command="ScrollBar.PageRightCommand"></RepeatButton>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem I am having is that even when I load up a ton of tabs the scrolling doesn't work. I don't think this is an issue with my styling. It appears that the tab headers aren't in the VisualTree or have a collapsed visibility. I've been going through the AvalonDock source for a while, but I can't see how the headers are hidden.
I've already had to subclass some of the AvalonDock classes because I needed additional properties on them.
Can someone either explain/help me come up with a solution to scroll the tabs?
Problem was solved very simply. I had to set the HorizontalScrollBarVisibility="Auto". I checked http://msdn.microsoft.com/en-us/library/system.windows.controls.scrollviewer.horizontalscrollbarvisibility.aspx to see what the default value is for this property, but didn't see one there or on the ScrollViewer page. I assume it defaults to "Hidden".
Anyhow, once set to "Auto" the repeat buttons are shown only when the tabs overflow.

Adding runtime label as content in checkbox wpf

I am dynamically creating the checkboxes at runtime and also applying the style at runtime. Designer has developed a checkbox like control that am applying at runtime. and he put a Label on that checkbox control to show the Text on the checkbox control as its content from the Database. But when i applying content of checkbox or label at runtime, it displays at the back of that checkbox control that is developed by the designer. How to make use of the Label control to show the content from the database on the checkbox control.
Kindly Suggest?
Thanks
It sounds to me like your designer forgot to include a <ContentPresenter/> in his checkbox template. If there is no ContentPresenter, the text you add as a comment will never be shown.
Here is an example of a custom CheckBox ControlTemplate that includes the required ContentPresenter:
<ControlTemplate TargetType="{x:Type CheckBox}">
<DockPanel>
<Border BorderThickness="1" BorderBrush="Black">
<Path x:Name="check" Width="10" Height="10"
Data=".... data for checkmark in checkbox ..." />
</Border>
<ContentPresenter/>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger ... trigger for changing checkmark ... />
</ControlTemplate.Triggers>
</ControlTemplate>
You should to review your checkbox style. It is supposed to be that label(textbox) in style liyng under checkbox mark. Here is nearly right code for the chekcbox template:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0">
<!--Place your mark here-->
</Border>
<Border Grid.Column="1">
<!--Place your label here-->
</Border>
</Grid>

Resources